python-ldap-2.4.10/0000755000076400001440000000000011764172736014640 5ustar michaelusers00000000000000python-ldap-2.4.10/Demo/0000755000076400001440000000000011764172736015524 5ustar michaelusers00000000000000python-ldap-2.4.10/Demo/sasl_bind.py0000644000076400001440000000363011243207222020013 0ustar michaelusers00000000000000# For documentation, see comments in Module/LDAPObject.c and the # ldap.sasl module documentation. import ldap,ldap.sasl ldap.sasl._trace_level=0 ldap.set_option(ldap.OPT_DEBUG_LEVEL,0) for ldap_uri,sasl_mech,sasl_cb_value_dict in [ ( "ldap://nb2.stroeder.local:1390/", 'CRAM-MD5', { ldap.sasl.CB_AUTHNAME :'fred', ldap.sasl.CB_PASS :'secret', } ), ( "ldap://nb2.stroeder.local:1390/", 'PLAIN', { ldap.sasl.CB_AUTHNAME :'fred', ldap.sasl.CB_PASS :'secret', } ), ( "ldap://nb2.stroeder.local:1390/", 'LOGIN', { ldap.sasl.CB_AUTHNAME :'fred', ldap.sasl.CB_PASS :'secret', } ), ( "ldapi://%2Ftmp%2Fopenldap-socket/", 'EXTERNAL', { } ), ( "ldap://nb2.stroeder.local:1390/", 'GSSAPI', { } ), ( "ldap://nb2.stroeder.local:1390/", 'NTLM', { ldap.sasl.CB_AUTHNAME :'fred', ldap.sasl.CB_PASS :'secret', } ), ( "ldap://nb2.stroeder.local:1390/", 'DIGEST-MD5', { ldap.sasl.CB_AUTHNAME :'fred', ldap.sasl.CB_PASS :'secret', } ), ]: sasl_auth = ldap.sasl.sasl(sasl_cb_value_dict,sasl_mech) print 20*'*',sasl_auth.mech,20*'*' # Open the LDAP connection l = ldap.initialize(ldap_uri,trace_level=0) # Set protocol version to LDAPv3 to enable SASL bind! l.protocol_version = 3 try: l.sasl_interactive_bind_s("", sasl_auth) except ldap.LDAPError,e: print 'Error using SASL mechanism',sasl_auth.mech,str(e) else: print 'Sucessfully bound using SASL mechanism:',sasl_auth.mech try: print 'Result of Who Am I? ext. op:',repr(l.whoami_s()) except ldap.LDAPError,e: print 'Error using SASL mechanism',sasl_auth.mech,str(e) try: print 'OPT_X_SASL_USERNAME',repr(l.get_option(ldap.OPT_X_SASL_USERNAME)) except AttributeError: pass l.unbind() del l python-ldap-2.4.10/Demo/schema_tree.py0000644000076400001440000000450710256025756020355 0ustar michaelusers00000000000000""" Outputs the object class tree read from LDAPv3 schema of a given server Usage: schema_oc_tree.py [--html] [LDAP URL] """ import sys,getopt,ldap,ldap.schema ldap.trace_level = 1 def PrintSchemaTree(schema,se_class,se_tree,se_oid,level): """ASCII text output for console""" se_obj = schema.get_obj(se_class,se_oid) if se_obj!=None: print '| '*(level-1)+'+---'*(level>0), \ ', '.join(se_obj.names), \ '(%s)' % se_obj.oid for sub_se_oid in se_tree[se_oid]: print '| '*(level+1) PrintSchemaTree(schema,se_class,se_tree,sub_se_oid,level+1) def HTMLSchemaTree(schema,se_class,se_tree,se_oid,level): """HTML output for browser""" se_obj = schema.get_obj(se_class,se_oid) if se_obj!=None: print """
%s (%s)
%s """ % (', '.join(se_obj.names),se_obj.oid,se_obj.desc) if se_tree[se_oid]: print '
' for sub_se_oid in se_tree[se_oid]: HTMLSchemaTree(schema,se_class,se_tree,sub_se_oid,level+1) print '
' print '
' ldap.set_option(ldap.OPT_DEBUG_LEVEL,0) ldap._trace_level = 0 subschemasubentry_dn,schema = ldap.schema.urlfetch(sys.argv[-1],ldap.trace_level) if subschemasubentry_dn is None: print 'No sub schema sub entry found!' sys.exit(1) try: options,args=getopt.getopt(sys.argv[1:],'',['html']) except getopt.error,e: print 'Error: %s\nUsage: schema_oc_tree.py [--html] [LDAP URL]' html_output = options and options[0][0]=='--html' oc_tree = schema.tree(ldap.schema.ObjectClass) at_tree = schema.tree(ldap.schema.AttributeType) #for k,v in oc_tree.items(): # print k,'->',v #for k,v in at_tree.items(): # print k,'->',v if html_output: print """ Object class tree

Object class tree

""" HTMLSchemaTree(schema,ldap.schema.ObjectClass,oc_tree,'2.5.6.0',0) print """

Attribute type tree

""" for a in schema.listall(ldap.schema.AttributeType): if at_tree[a]: HTMLSchemaTree(schema,ldap.schema.AttributeType,at_tree,a,0) print print """
""" else: print '*** Object class tree ***\n' print PrintSchemaTree(schema,ldap.schema.ObjectClass,oc_tree,'2.5.6.0',0) print '\n*** Attribute types tree ***\n' PrintSchemaTree(schema,ldap.schema.AttributeType,at_tree,'_',0) python-ldap-2.4.10/Demo/simplebrowse.py0000644000076400001440000000615107410130511020566 0ustar michaelusers00000000000000#! python # # simple LDAP server browsing example # import ldap import string from traceback import print_exc url = "ldap://ldap.openldap.org/" dn = "dc=openldap,dc=org" print "Connecting to", url l = ldap.initialize(url) l.bind_s("", "", ldap.AUTH_SIMPLE); lastdn = dn dnlist = None while 1: #-- read a command try: cmd = raw_input(dn + "> ") except EOFError: print break try: if cmd == "?": print "cd - change DN to " print "cd - change DN to number of last 'ls'" print "cd - - change to previous DN" print "cd .. - change to one-level higher DN" print "cd - change to root DN" print "ls - list children of crrent DN" print ". - show attributes of current DN" print "/ - list descendents matching filter " print "? - show this help" elif cmd == "ls": print "Children of", `dn`, ":" dnlist = [] # # List the children at one level down from the current dn # We use the filter 'objectclass=*' to match everything. # We're not interested in attributes at this stage, so # we specify [] as the list of attribute names to retreive. # for name,attrs in l.search_s(dn, ldap.SCOPE_ONELEVEL, "objectclass=*", []): #-- shorten resulting dns for output brevity if name.startswith(dn+", "): shortname = "+ "+name[len(dn)+2:] elif name.endswith(", "+dn): shortname = name[:-len(dn)-2]+" +" else: shortname = name print " %3d. %s" % (len(dnlist), shortname) dnlist.append(name) elif cmd == "cd": dn = "" dnlist = None elif cmd.startswith("cd "): arg = cmd[3:] if arg == '-': lastdn,dn = dn,lastdn elif arg == '..': dn = string.join(ldap.explode_dn(dn)[1:], ",") dn = string.strip(dn) else: try: i = int(arg) except: godn = arg else: if dnlist is None: print "do an ls first" else: godn = dnlist[i] lastdn = dn dn = godn elif cmd == ".": # # Retrieve all the attributes for the current dn. # We construct a search using SCOPE_BASE (ie just the # given DN) and again filter with "objectclass=*". # No attributes are listed, so the default is for # the client to receive all attributes on the DN. # print "Attributes of", `dn`, ":" for name,attrs in l.search_s(dn, ldap.SCOPE_BASE, "objectclass=*"): print " %-24s" % name for k,vals in attrs.items(): for v in vals: if len(v) > 200: v = `v[:200]` + \ ("... (%d bytes)" % len(v)) else: v = `v` print " %-12s: %s" % (k, v) elif cmd.startswith("/"): # # Search descendent objects to match a given filter. # We use SCOPE_SUBTREE to indicate descendents, and # again specify an empty attribute list to indicate # that we're not interested in them. # expr = cmd[1:] print "Descendents matching filter", `expr`, ":" for name,attrs in l.search_s(dn, ldap.SCOPE_SUBTREE, expr, []): print " %24s", name else: print "unknown command - try '?' for help" except: print_exc() python-ldap-2.4.10/Demo/.cvsignore0000644000076400001440000000002507507672213017514 0ustar michaelusers00000000000000*.pyc *.pyo *.bck *~ python-ldap-2.4.10/Demo/pyasn1/0000755000076400001440000000000011764172736016737 5ustar michaelusers00000000000000python-ldap-2.4.10/Demo/pyasn1/readentrycontrol.py0000644000076400001440000000660611573756502022714 0ustar michaelusers00000000000000#!/usr/bin/env python """ This sample script demonstrates the use of the pre-read control (see RFC 4527). Originally contributed by Andreas Hasenack Requires module pyasn1 (see http://pyasn1.sourceforge.net/) """ import pprint,ldap,ldap.modlist from ldap.controls.readentry import PreReadControl,PostReadControl uri = "ldap://localhost:2071/" l = ldap.initialize(uri,trace_level=2) l.simple_bind_s('uid=diradm,ou=schulung,dc=stroeder,dc=local','testsecret') print """#--------------------------------------------------------------------------- # Add new entry #--------------------------------------------------------------------------- """ new_test_dn = "uid=ablume,ou=Users,ou=schulung,dc=stroeder,dc=local" new_test_dn2 = "uid=ablume2,ou=Users,ou=schulung,dc=stroeder,dc=local" new_test_entry = { 'objectClass':['account','posixAccount'], 'uid':['ablume'], 'cn':['Anna Blume'], 'uidNumber':['10000'], 'gidNumber':['10000'], 'homeDirectory':['/home/ablume'], } pr = PostReadControl(criticality=True,attrList=['entryUUID','entryCSN']) msg_id = l.add_ext( new_test_dn, ldap.modlist.addModlist(new_test_entry), serverctrls = [pr] ) _,_,_,resp_ctrls = l.result3(msg_id) print "resp_ctrls[0].dn:",resp_ctrls[0].dn print "resp_ctrls[0].entry:";pprint.pprint(resp_ctrls[0].entry) print """#--------------------------------------------------------------------------- # Modify entry #--------------------------------------------------------------------------- """ pr = PreReadControl(criticality=True,attrList=['uidNumber','gidNumber','entryCSN']) msg_id = l.modify_ext( new_test_dn, [(ldap.MOD_INCREMENT, "uidNumber", "1"),(ldap.MOD_INCREMENT, "gidNumber", "1")], serverctrls = [pr] ) _,_,_,resp_ctrls = l.result3(msg_id) print "resp_ctrls[0].dn:",resp_ctrls[0].dn print "resp_ctrls[0].entry:";pprint.pprint(resp_ctrls[0].entry) pr = PostReadControl(criticality=True,attrList=['uidNumber','gidNumber','entryCSN']) msg_id = l.modify_ext( new_test_dn, [(ldap.MOD_INCREMENT, "uidNumber", "1"),(ldap.MOD_INCREMENT, "gidNumber", "1")], serverctrls = [pr] ) _,_,_,resp_ctrls = l.result3(msg_id) print "resp_ctrls[0].dn:",resp_ctrls[0].dn print "resp_ctrls[0].entry:";pprint.pprint(resp_ctrls[0].entry) print """#--------------------------------------------------------------------------- # Rename entry #--------------------------------------------------------------------------- """ pr = PostReadControl(criticality=True,attrList=['uid']) msg_id = l.rename( new_test_dn, "uid=ablume2", delold=1, serverctrls = [pr] ) _,_,_,resp_ctrls = l.result3(msg_id) print "resp_ctrls[0].dn:",resp_ctrls[0].dn print "resp_ctrls[0].entry:";pprint.pprint(resp_ctrls[0].entry) pr = PreReadControl(criticality=True,attrList=['uid']) msg_id = l.rename( new_test_dn2, "uid=ablume", delold=1, serverctrls = [pr] ) _,_,_,resp_ctrls = l.result3(msg_id) print "resp_ctrls[0].dn:",resp_ctrls[0].dn print "resp_ctrls[0].entry:";pprint.pprint(resp_ctrls[0].entry) print """#--------------------------------------------------------------------------- # Delete entry #--------------------------------------------------------------------------- """ pr = PreReadControl(criticality=True,attrList=['*','+']) msg_id = l.delete_ext( new_test_dn, serverctrls = [pr] ) _,_,_,resp_ctrls = l.result3(msg_id) print "resp_ctrls[0].dn:",resp_ctrls[0].dn print "resp_ctrls[0].entry:";pprint.pprint(resp_ctrls[0].entry) python-ldap-2.4.10/Demo/pyasn1/dds.py0000644000076400001440000000251711704305353020053 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ Demo script for Dynamic Entries (see RFC 2589) This needs the following software: Python pyasn1 pyasn1-modules python-ldap 2.4+ """ from ldap.extop.dds import RefreshRequest,RefreshResponse import sys,ldap,ldapurl,getpass try: ldap_url = ldapurl.LDAPUrl(sys.argv[1]) request_ttl = int(sys.argv[2]) except IndexError,ValueError: print 'Usage: dds.py ' sys.exit(1) # Set debugging level #ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) ldapmodule_trace_level = 2 ldapmodule_trace_file = sys.stderr ldap_conn = ldap.ldapobject.LDAPObject( ldap_url.initializeUrl(), trace_level=ldapmodule_trace_level, trace_file=ldapmodule_trace_file ) if ldap_url.cred is None: print 'Password for %s:' % (repr(ldap_url.who)) ldap_url.cred = getpass.getpass() try: ldap_conn.simple_bind_s(ldap_url.who or '',ldap_url.cred or '') except ldap.INVALID_CREDENTIALS,e: print 'Simple bind failed:',str(e) sys.exit(1) else: extreq = RefreshRequest(entryName=ldap_url.dn,requestTtl=request_ttl) try: extop_resp_obj = ldap_conn.extop_s(extreq,extop_resp_class=RefreshResponse) except ldap.LDAPError,e: print str(e) else: if extop_resp_obj.responseTtl!=request_ttl: print 'Different response TTL:',extop_resp_obj.responseTtl else: print 'Response TTL:',extop_resp_obj.responseTtl python-ldap-2.4.10/Demo/pyasn1/psearch.py0000644000076400001440000000406711552002163020722 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ Demo script for Persistent Search Control (see http://tools.ietf.org/html/draft-ietf-ldapext-psearch) See http://www.python-ldap.org/ for project details. This needs the following software: Python pyasn1 pyasn1-modules python-ldap 2.4+ """ import sys,ldap,ldapurl,getpass from ldap.controls.psearch import PersistentSearchControl,EntryChangeNotificationControl,CHANGE_TYPES_STR try: ldap_url = ldapurl.LDAPUrl(sys.argv[1]) except IndexError: print 'Usage: psearch.py ' sys.exit(1) # Set debugging level #ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) ldapmodule_trace_level = 2 ldapmodule_trace_file = sys.stderr ldap_conn = ldap.ldapobject.LDAPObject( ldap_url.initializeUrl(), trace_level=ldapmodule_trace_level, trace_file=ldapmodule_trace_file ) if ldap_url.cred is None: print 'Password for %s:' % (repr(ldap_url.who)) ldap_url.cred = getpass.getpass() try: ldap_conn.simple_bind_s(ldap_url.who,ldap_url.cred) except ldap.INVALID_CREDENTIALS,e: print 'Simple bind failed:',str(e) sys.exit(1) psc = PersistentSearchControl() msg_id = ldap_conn.search_ext( ldap_url.dn, ldap_url.scope, ldap_url.filterstr, attrlist = ldap_url.attrs or ['*','+'], serverctrls=[psc], ) while True: try: res_type,res_data,res_msgid,_,_,_ = ldap_conn.result4( msg_id, all=0, timeout=10.0, add_ctrls=1, add_intermediates=1, resp_ctrl_classes={EntryChangeNotificationControl.controlType:EntryChangeNotificationControl}, ) except ldap.TIMEOUT: print 'Timeout waiting for results...' else: for dn,entry,srv_ctrls in res_data: ecn_ctrls = [ c for c in srv_ctrls if c.controlType == EntryChangeNotificationControl.controlType ] if ecn_ctrls: changeType,previousDN,changeNumber = ecn_ctrls[0].changeType,ecn_ctrls[0].previousDN,ecn_ctrls[0].changeNumber change_type_desc = CHANGE_TYPES_STR[changeType] print 'changeType: %s (%d), changeNumber: %s, previousDN: %s' % (change_type_desc,changeType,changeNumber,repr(previousDN)) python-ldap-2.4.10/Demo/pyasn1/ppolicy.py0000644000076400001440000000274111663516773020775 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ Demo script for Password Policy Controls (see http://tools.ietf.org/html/draft-behera-ldap-password-policy) This needs the following software: Python pyasn1 pyasn1-modules python-ldap 2.4+ """ import sys,ldap,ldapurl,getpass from ldap.controls.ppolicy import PasswordPolicyError,PasswordPolicyControl try: ldap_url = ldapurl.LDAPUrl(sys.argv[1]) except IndexError,ValueError: print 'Usage: ppolicy.py ' sys.exit(1) # Set debugging level #ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) ldapmodule_trace_level = 2 ldapmodule_trace_file = sys.stderr ldap_conn = ldap.ldapobject.LDAPObject( ldap_url.initializeUrl(), trace_level=ldapmodule_trace_level, trace_file=ldapmodule_trace_file ) if ldap_url.cred is None: print 'Password for %s:' % (repr(ldap_url.who)) ldap_url.cred = getpass.getpass() try: msgid = ldap_conn.simple_bind(ldap_url.who,ldap_url.cred,serverctrls=[PasswordPolicyControl()]) res_type,res_data,res_msgid,res_ctrls = ldap_conn.result3(msgid) except ldap.INVALID_CREDENTIALS,e: print 'Simple bind failed:',str(e) sys.exit(1) else: if res_ctrls[0].controlType==PasswordPolicyControl.controlType: ppolicy_ctrl = res_ctrls[0] print 'PasswordPolicyControl' print 'error',repr(ppolicy_ctrl.error),(ppolicy_ctrl.error!=None)*repr(PasswordPolicyError(ppolicy_ctrl.error)) print 'timeBeforeExpiration',repr(ppolicy_ctrl.timeBeforeExpiration) print 'graceAuthNsRemaining',repr(ppolicy_ctrl.graceAuthNsRemaining) python-ldap-2.4.10/Demo/pyasn1/syncrepl.py0000644000076400001440000001302411764172557021151 0ustar michaelusers00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- """ This script implements a syncrepl consumer which syncs data from an OpenLDAP server to a local (shelve) database. Notes: The bound user needs read access to the attributes entryDN and entryCSN. This needs the following software: Python pyasn1 0.1.4+ pyasn1-modules python-ldap 2.4.10+ """ # Import the python-ldap modules import ldap,ldapurl # Import specific classes from python-ldap from ldap.ldapobject import ReconnectLDAPObject from ldap.syncrepl import SyncreplConsumer # Import modules from Python standard lib import shelve,signal,time,sys,logging # Global state watcher_running = True ldap_connection = False class SyncReplConsumer(ReconnectLDAPObject,SyncreplConsumer): """ Syncrepl Consumer interface """ def __init__(self,db_path,*args,**kwargs): # Initialise the LDAP Connection first ldap.ldapobject.ReconnectLDAPObject.__init__(self, *args, **kwargs) # Now prepare the data store self.__data = shelve.open(db_path, 'c') # We need this for later internal use self.__presentUUIDs = dict() def __del__(self): # Close the data store properly to avoid corruption self.__data.close() def syncrepl_get_cookie(self): if 'cookie' in self.__data: return self.__data['cookie'] def syncrepl_set_cookie(self,cookie): self.__data['cookie'] = cookie def syncrepl_entry(self,dn,attributes,uuid): # First we determine the type of change we have here (and store away the previous data for later if needed) previous_attributes = dict() if uuid in self.__data: change_type = 'modify' previous_attributes = self.__data[uuid] else: change_type = 'add' # Now we store our knowledge of the existence of this entry (including the DN as an attribute for convenience) attributes['dn'] = dn self.__data[uuid] = attributes # Debugging print 'Detected', change_type, 'of entry:', dn # If we have a cookie then this is not our first time being run, so it must be a change if 'ldap_cookie' in self.__data: self.perform_application_sync(dn, attributes, previous_attributes) def syncrepl_delete(self,uuids): # Make sure we know about the UUID being deleted, just in case... uuids = [uuid for uuid in uuids if uuid in self.__data] # Delete all the UUID values we know of for uuid in uuids: print 'Detected deletion of entry:', self.__data[uuid]['dn'] del self.__data[uuid] def syncrepl_present(self,uuids,refreshDeletes=False): # If we have not been given any UUID values, then we have recieved all the present controls... if uuids is None: # We only do things if refreshDeletes is false as the syncrepl extension will call syncrepl_delete instead when it detects a delete notice if refreshDeletes is False: deletedEntries = [uuid for uuid in self.__data.keys() if uuid not in self.__presentUUIDs and uuid != 'ldap_cookie'] self.syncrepl_delete( deletedEntries ) # Phase is now completed, reset the list self.__presentUUIDs = {} else: # Note down all the UUIDs we have been sent for uuid in uuids: self.__presentUUIDs[uuid] = True def perform_application_sync(self,dn,attributes,previous_attributes): print 'Performing application sync for:', dn return True # Shutdown handler def commenceShutdown(signum, stack): # Declare the needed global variables global watcher_running, ldap_connection print 'Shutting down!' # We are no longer running watcher_running = False # Tear down the server connection if( ldap_connection ): del ldap_connection # Shutdown sys.exit(0) # Time to actually begin execution # Install our signal handlers signal.signal(signal.SIGTERM,commenceShutdown) signal.signal(signal.SIGINT,commenceShutdown) try: ldap_url = ldapurl.LDAPUrl(sys.argv[1]) database_path = sys.argv[2] except IndexError,e: print 'Usage: syncrepl-client.py ' sys.exit(1) except ValueError,e: print 'Error parsing command-line arguments:',str(e) sys.exit(1) while watcher_running: print 'Connecting to LDAP server now...' # Prepare the LDAP server connection (triggers the connection as well) ldap_connection = SyncReplConsumer(database_path,ldap_url.initializeUrl()) # Now we login to the LDAP server try: ldap_connection.simple_bind_s(ldap_url.who,ldap_url.cred) except ldap.INVALID_CREDENTIALS, e: print 'Login to LDAP server failed: ', str(e) sys.exit(1) except ldap.SERVER_DOWN: print 'LDAP server is down, going to retry.' time.sleep(5) continue # Commence the syncing print 'Commencing sync process' ldap_search = ldap_connection.syncrepl_search( ldap_url.dn or '', ldap_url.scope or ldap.SCOPE_SUBTREE, mode = 'refreshAndPersist', filterstr = ldap_url.filterstr or '(objectClass=*)' ) try: while ldap_connection.syncrepl_poll( all = 1, msgid = ldap_search): pass except KeyboardInterrupt: # User asked to exit commenceShutdown() pass except Exception, e: # Handle any exception if watcher_running: print 'Encountered a problem, going to retry. Error:', str(e) time.sleep(5) pass python-ldap-2.4.10/Demo/pyasn1/README0000644000076400001440000000027411544144266017613 0ustar michaelusers00000000000000The sample modules/scripts herein require modules pyasn1 and pyasn1-modules. http://pyasn1.sourceforge.net/ http://pypi.python.org/pypi/pyasn1 http://pypi.python.org/pypi/pyasn1-modules python-ldap-2.4.10/Demo/pyasn1/sessiontrack.py0000644000076400001440000000251711604613510022005 0ustar michaelusers00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- """ demo_track_ldap_session.py Client-seitige Demo-Implementierung von Session Tracking Control http://tools.ietf.org/html/draft-wahl-ldap-session-03 """ __version__ = '0.1' import sys,getpass,ldap,ldapurl from ldap.controls.sessiontrack import SessionTrackingControl,SESSION_TRACKING_FORMAT_OID_USERNAME try: ldap_url = ldapurl.LDAPUrl(sys.argv[1]) except IndexError,ValueError: print 'Usage: %s ' % (sys.argv[0]) sys.exit(1) # Set debugging level #ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) ldapmodule_trace_level = 2 ldapmodule_trace_file = sys.stderr ldap_conn = ldap.ldapobject.LDAPObject( ldap_url.initializeUrl(), trace_level=ldapmodule_trace_level, trace_file=ldapmodule_trace_file ) if ldap_url.who and ldap_url.cred is None: print 'Password for %s:' % (repr(ldap_url.who)) ldap_url.cred = getpass.getpass() try: ldap_conn.simple_bind_s(ldap_url.who or '',ldap_url.cred or '') except ldap.INVALID_CREDENTIALS,e: print 'Simple bind failed:',str(e) sys.exit(1) st_ctrl = SessionTrackingControl( '192.0.2.1', 'app.example.com', SESSION_TRACKING_FORMAT_OID_USERNAME, 'bloggs' ) ldap_conn.search_ext_s( ldap_url.dn or '', ldap_url.scope or ldap.SCOPE_SUBTREE, ldap_url.filterstr or '(objectClass=*)', ldap_url.attrs or ['*'], serverctrls=[st_ctrl] ) python-ldap-2.4.10/Demo/pickle_ldapobject.py0000644000076400001440000000063107650130021021510 0ustar michaelusers00000000000000import os,ldap,pickle temp_file_name = os.path.join(os.environ.get('TMP','/tmp'),'pickle_ldap-%d' % (os.getpid())) l1 = ldap.ldapobject.ReconnectLDAPObject('ldap://localhost:1390',trace_level=1) l1.protocol_version = 3 l1.search_s('',ldap.SCOPE_BASE,'(objectClass=*)') pickle.dump(l1,open(temp_file_name,'wb')) l2 = pickle.load(open(temp_file_name,'rb')) l2.search_s('',ldap.SCOPE_BASE,'(objectClass=*)') python-ldap-2.4.10/Demo/passwd_ext_op.py0000644000076400001440000000141510137547500020742 0ustar michaelusers00000000000000""" Example showing the use of the password extended operation. """ import sys,ldap,ldapurl,getpass # Set debugging level ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) ldapmodule_trace_level = 2 ldapmodule_trace_file = sys.stderr lu = ldapurl.LDAPUrl(sys.argv[1]) print 'Old password' oldpw = getpass.getpass() print 'New password' newpw = getpass.getpass() # Set path name of file containing all CA certificates # needed to validate server certificates ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,'/etc/httpd/ssl.crt/myCA-cacerts.pem') # Create LDAPObject instance l = ldap.initialize(lu.initializeUrl(),trace_level=ldapmodule_trace_level,trace_file=ldapmodule_trace_file) l.protocol_version=ldap.VERSION3 l.simple_bind_s(lu.dn,oldpw) l.passwd(lu.dn,oldpw,newpw) l.unbind_s() python-ldap-2.4.10/Demo/schema.py0000644000076400001440000000362511605554276017341 0ustar michaelusers00000000000000import sys,ldap,ldap.schema schema_attrs = ldap.schema.SCHEMA_ATTRS ldap.set_option(ldap.OPT_DEBUG_LEVEL,0) ldap._trace_level = 0 subschemasubentry_dn,schema = ldap.schema.urlfetch(sys.argv[-1]) if subschemasubentry_dn is None: print 'No sub schema sub entry found!' sys.exit(1) if schema.non_unique_oids: print '*** Schema errors ***' print 'non-unique OIDs:\n','\r\n'.join(schema.non_unique_oids) print '*** Schema from',repr(subschemasubentry_dn) # Display schema for attr_type,schema_class in ldap.schema.SCHEMA_CLASS_MAPPING.items(): print '*'*20,attr_type,'*'*20 for element_id in schema.listall(schema_class): se_orig = schema.get_obj(schema_class,element_id) print attr_type,str(se_orig) print '*** Testing object class inetOrgPerson ***' drink = schema.get_obj(ldap.schema.AttributeType,'favouriteDrink') if not drink is None: print '*** drink ***' print 'drink.names',repr(drink.names) print 'drink.collective',repr(drink.collective) inetOrgPerson = schema.get_obj(ldap.schema.ObjectClass,'inetOrgPerson') if not inetOrgPerson is None: print inetOrgPerson.must,inetOrgPerson.may print '*** person,organizationalPerson,inetOrgPerson ***' try: print schema.attribute_types( ['person','organizationalPerson','inetOrgPerson'] ) print schema.attribute_types( ['person','organizationalPerson','inetOrgPerson'], attr_type_filter = [ ('no_user_mod',[0]), ('usage',range(2)), ] ) except KeyError,e: print '***KeyError',str(e) schema.ldap_entry() print str(schema.get_obj(ldap.schema.MatchingRule,'2.5.13.0')) print str(schema.get_obj(ldap.schema.MatchingRuleUse,'2.5.13.0')) print str(schema.get_obj(ldap.schema.AttributeType,'name')) print str(schema.get_inheritedobj(ldap.schema.AttributeType,'cn',['syntax','equality','substr','ordering'])) must_attr,may_attr = schema.attribute_types(['person','organizationalPerson','inetOrgPerson'],raise_keyerror=0) python-ldap-2.4.10/Demo/simple.py0000644000076400001440000000450607426477316017376 0ustar michaelusers00000000000000import sys,getpass import ldap #l = ldap.open("localhost", 31001) l = ldap.open("marta.it.uq.edu.au") login_dn = "cn=root,ou=CSEE,o=UQ,c=AU" login_pw = getpass.getpass("Password for %s: " % login_dn) l.simple_bind_s(login_dn, login_pw) # # create a new sub organisation # try: dn = "ou=CSEE,o=UQ,c=AU" print "Adding", repr(dn) l.add_s(dn, [ ("objectclass",["organizationalUnit"]), ("ou", ["CSEE"]), ("description", [ "Department of Computer Science and Electrical Engineering"]), ] ) except _ldap.LDAPError: pass # # create an entry for me # dn = "cn=David Leonard,ou=CSEE,o=UQ,c=AU" print "Updating", repr(dn) try: l.delete_s(dn) except: pass l.add_s(dn, [ ("objectclass", ["organizationalPerson"]), ("sn", ["Leonard"]), ("cn", ["David Leonard"]), ("description", ["Ph.D. student"]), ("display-name", ["David Leonard"]), #("commonname", ["David Leonard"]), ("mail", ["david.leonard@csee.uq.edu.au"]), ("othermailbox", ["d@openbsd.org"]), ("givenname", ["David"]), ("surname", ["Leonard"]), ("seeAlso", ["http://www.csee.uq.edu.au/~leonard/"]), ("url", ["http://www.csee.uq.edu.au/~leonard/"]), #("homephone", []), #("fax", []), #("otherfacsimiletelephonenumber",[]), #("officefax", []), #("mobile", []), #("otherpager", []), #("officepager", []), #("pager", []), ("info", ["info"]), ("title", ["Mr"]), #("telephonenumber", []), ("l", ["Brisbane"]), ("st", ["Queensland"]), ("c", ["AU"]), ("co", ["co"]), ("o", ["UQ"]), ("ou", ["CSEE"]), #("homepostaladdress", []), #("postaladdress", []), #("streetaddress", []), #("street", []), ("department", ["CSEE"]), ("comment", ["comment"]), #("postalcode", []), ("physicaldeliveryofficename", ["Bldg 78, UQ, St Lucia"]), ("preferredDeliveryMethod", ["email"]), ("initials", ["DRL"]), ("conferenceinformation", ["MS-conferenceinformation"]), #("usercertificate", []), ("labeleduri", ["labeleduri"]), ("manager", ["cn=Jaga Indulska"]), ("reports", ["reports"]), ("jpegPhoto", [open("/www/leonard/leonard.jpg","r").read()]), ("uid", ["leonard"]), ("userPassword", [""]) ]) # # search beneath the CSEE/UQ/AU tree # res = l.search_s( "ou=CSEE, o=UQ, c=AU", _ldap.SCOPE_SUBTREE, "objectclass=*", ) print res l.unbind() python-ldap-2.4.10/Demo/ldapcontrols.py0000644000076400001440000000147710256025543020577 0ustar michaelusers00000000000000import ldap,ldapurl,pprint from ldap.controls import LDAPControl,BooleanControl l = ldap.initialize('ldap://localhost:1390',trace_level=2) print 60*'#' pprint.pprint(l.get_option(ldap.OPT_SERVER_CONTROLS)) l.manage_dsa_it(1,1) pprint.pprint(l.get_option(ldap.OPT_SERVER_CONTROLS)) print 60*'#' # Search with ManageDsaIT control (which has no value) pprint.pprint(l.search_ext_s( 'cn=Test-Referral,ou=Testing,dc=stroeder,dc=de', ldap.SCOPE_BASE, '(objectClass=*)', ['*','+'], serverctrls = [ LDAPControl('2.16.840.1.113730.3.4.2',1,None) ], )) print 60*'#' # Search with Subentries control (which has boolean value) pprint.pprint(l.search_ext_s( 'dc=stroeder,dc=de', ldap.SCOPE_SUBTREE, '(objectClass=subentry)', ['*','+'], serverctrls = [ BooleanControl('1.3.6.1.4.1.4203.1.10.1',1,1) ], )) print 60*'#' python-ldap-2.4.10/Demo/resiter.py0000644000076400001440000000130110333634551017533 0ustar michaelusers00000000000000""" Demo for using ldap.resiter.ResultProcessor written by Michael Stroeder See http://python-ldap.sourceforge.net for details. \$Id: resiter.py,v 1.1 2005/11/07 11:24:25 stroeder Exp $ Python compability note: Requires Python 2.3+ """ import ldap,ldap.resiter class LDAPObject(ldap.ldapobject.LDAPObject,ldap.resiter.ResultProcessor): pass l = LDAPObject('ldap://localhost:1390',trace_level=1) l.protocol_version = 3 msgid = l.search('dc=stroeder,dc=de',ldap.SCOPE_SUBTREE,'(cn=m*)') result_iter = l.allresults(msgid) for result_type,result_list,result_msgid,result_serverctrls in result_iter: print result_type,result_list,result_msgid,result_serverctrls l.unbind_s() python-ldap-2.4.10/Demo/matchedvalues.py0000644000076400001440000000355410655566727020737 0ustar michaelusers00000000000000#!/usr/bin/env python # # demo for matched values control (RFC 3876) # # suppose the uid=jsmith LDAP entry has two mail attributes: # # dn: uid=jsmith,ou=People,dc=example,dc=com # (...) # mail: jsmith@example.com # mail: jsmith@example.org # # Let's say you want to fetch only the example.org email. Without MV, # you would first fetch all mail attributes and then filter them further # on the client. With the MV control, the result can be given to the # client already filtered. # # Sample output: # $ ./matchedvalues.py # LDAP filter used: (&(objectClass=inetOrgPerson)(mail=*@example.org)) # Requesting 'mail' attribute back # # No matched values control: # dn: uid=jsmith,ou=People,dc=example,dc=com # mail: jsmith@example.org # mail: john@example.com # # Matched values control: (mail=*@example.org) # dn: uid=jsmith,ou=People,dc=example,dc=com # mail: jsmith@example.org import ldap from ldap.controls import MatchedValuesControl def print_result(search_result): for n in range(len(search_result)): print "dn: %s" % search_result[n][0] for attr in search_result[n][1].keys(): for i in range(len(search_result[n][1][attr])): print "%s: %s" % (attr, search_result[n][1][attr][i]) print uri = "ldap://ldap.example.com" base = "dc=example,dc=com" scope = ldap.SCOPE_SUBTREE filter = "(&(objectClass=inetOrgPerson)(mail=*@example.org))" control_filter = "(mail=*@example.org)" ld = ldap.initialize(uri) mv = MatchedValuesControl(criticality=True, controlValue=control_filter) res = ld.search_ext_s(base, scope, filter, attrlist = ['mail']) print "LDAP filter used: %s" % filter print "Requesting 'mail' attribute back" print print "No matched values control:" print_result(res) res = ld.search_ext_s(base, scope, filter, attrlist = ['mail'], serverctrls = [mv]) print "Matched values control: %s" % control_filter print_result(res) python-ldap-2.4.10/Demo/initialize.py0000644000076400001440000000545611764172557020252 0ustar michaelusers00000000000000""" Various examples how to connect to a LDAP host with the new factory function ldap.initialize() introduced in OpenLDAP 2 API. Assuming you have LDAP servers running on ldap://localhost:1390 (LDAP with StartTLS) ldaps://localhost:1391 (LDAP over SSL) ldapi://%2ftmp%2fopenldap2 (domain socket /tmp/openldap2) """ import sys,os,ldap # Switch off processing .ldaprc or ldap.conf os.environ['LDAPNOINIT']='1' # Set debugging level #ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) ldapmodule_trace_level = 1 ldapmodule_trace_file = sys.stderr ldap._trace_level = ldapmodule_trace_level # Complete path name of the file containing all trusted CA certs CACERTDIR='/etc/ssl/certs' print """################################################################## # LDAPv3 connection with StartTLS ext. op. ################################################################## """ # Create LDAPObject instance l = ldap.initialize('ldap://localhost:1390',trace_level=ldapmodule_trace_level,trace_file=ldapmodule_trace_file) # Set LDAP protocol version used l.protocol_version=ldap.VERSION3 # Force cert validation l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_DEMAND) # Set path name of file containing all trusted CA certificates l.set_option(ldap.OPT_X_TLS_CACERTDIR,CACERTDIR) # Force libldap to create a new SSL context (must be last TLS option!) l.set_option(ldap.OPT_X_TLS_NEWCTX,0) # Now try StartTLS extended operation l.start_tls_s() # Try an explicit anon bind to provoke failure l.simple_bind_s('','') # Close connection l.unbind_s() print """################################################################## # LDAPv3 connection over SSL ################################################################## """ # Create LDAPObject instance l = ldap.initialize('ldaps://localhost:1391',trace_level=ldapmodule_trace_level,trace_file=ldapmodule_trace_file) # Set LDAP protocol version used l.protocol_version=ldap.VERSION3 # Force cert validation l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_DEMAND) # Set path name of file containing all trusted CA certificates l.set_option(ldap.OPT_X_TLS_CACERTDIR,CACERTDIR) # Force libldap to create a new SSL context (must be last TLS option!) l.set_option(ldap.OPT_X_TLS_NEWCTX,0) # Try an explicit anon bind to provoke failure l.simple_bind_s('','') # Close connection l.unbind_s() print """################################################################## # LDAPv3 connection over Unix domain socket ################################################################## """ # Create LDAPObject instance l = ldap.initialize('ldapi://%2ftmp%2fopenldap-socket',trace_level=ldapmodule_trace_level,trace_file=ldapmodule_trace_file) # Set LDAP protocol version used l.protocol_version=ldap.VERSION3 # Try an explicit anon bind to provoke failure l.simple_bind_s('','') # Close connection l.unbind_s() python-ldap-2.4.10/Demo/rename.py0000644000076400001440000000203107426477316017343 0ustar michaelusers00000000000000import ldap from getpass import getpass # Create LDAPObject instance l = ldap.initialize('ldap://localhost:1389',trace_level=1) print 'Password:' cred = getpass() try: # Set LDAP protocol version used l.set_option(ldap.OPT_PROTOCOL_VERSION,3) # Try a bind to provoke failure if protocol version is not supported l.bind_s('cn=root,dc=stroeder,dc=com',cred,ldap.AUTH_SIMPLE) print 'Using rename_s():' l.rename_s( 'uid=fred,ou=Unstructured testing tree,dc=stroeder,dc=com', 'cn=Fred Feuerstein', 'dc=stroeder,dc=com', 0 ) l.rename_s( 'cn=Fred Feuerstein,dc=stroeder,dc=com', 'uid=fred', 'ou=Unstructured testing tree,dc=stroeder,dc=com', 0 ) m = l.rename( 'uid=fred,ou=Unstructured testing tree,dc=stroeder,dc=com', 'cn=Fred Feuerstein', 'dc=stroeder,dc=com', 0 ) r = l.result(m,1) m = l.rename( 'cn=Fred Feuerstein,dc=stroeder,dc=com', 'uid=fred', 'ou=Unstructured testing tree,dc=stroeder,dc=com', 0 ) r = l.result(m,1) finally: l.unbind_s() python-ldap-2.4.10/Demo/reconnect.py0000644000076400001440000000103107524237466020052 0ustar michaelusers00000000000000import sys,time,ldap,ldap.ldapobject,ldapurl from ldap.ldapobject import * ldap_url = ldapurl.LDAPUrl(sys.argv[1]) ldap_url.applyDefaults({ 'who':'', 'cred':'', 'filterstr':'(objectClass=*)', 'scope':ldap.SCOPE_BASE }) ldap.trace_level=1 l = ldap.ldapobject.ReconnectLDAPObject( ldap_url.initializeUrl(),trace_level=ldap.trace_level ) l.protocol_version = ldap.VERSION3 l.simple_bind_s(ldap_url.who,ldap_url.cred) while 1: l.search_s(ldap_url.dn,ldap_url.scope,ldap_url.filterstr,ldap_url.attrs) sys.stdin.readline() python-ldap-2.4.10/Demo/page_control.py0000644000076400001440000000331211545152005020531 0ustar michaelusers00000000000000url = "ldap://localhost:1390/" base = "dc=stroeder,dc=de" search_flt = r'(objectClass=*)' page_size = 10 import ldap,pprint from ldap.controls import SimplePagedResultsControl searchreq_attrlist=['cn','entryDN','entryUUID','mail','objectClass'] #ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) ldap.set_option(ldap.OPT_REFERRALS, 0) l = ldap.initialize(url,trace_level=1) l.protocol_version = 3 l.simple_bind_s("", "") req_ctrl = SimplePagedResultsControl(True,size=page_size,cookie='') known_ldap_resp_ctrls = { SimplePagedResultsControl.controlType:SimplePagedResultsControl, } # Send search request msgid = l.search_ext( base, ldap.SCOPE_SUBTREE, search_flt, attrlist=searchreq_attrlist, serverctrls=[req_ctrl] ) pages = 0 while True: pages += 1 print "Getting page %d" % (pages) rtype, rdata, rmsgid, serverctrls = l.result3(msgid,resp_ctrl_classes=known_ldap_resp_ctrls) print '%d results' % len(rdata) print 'serverctrls=',pprint.pprint(serverctrls) # pprint.pprint(rdata) pctrls = [ c for c in serverctrls if c.controlType == SimplePagedResultsControl.controlType ] if pctrls: print 'pctrls[0].size',repr(pctrls[0].size) print 'pctrls[0].cookie',repr(pctrls[0].cookie) if pctrls[0].cookie: # Copy cookie from response control to request control req_ctrl.cookie = pctrls[0].cookie msgid = l.search_ext( base, ldap.SCOPE_SUBTREE, search_flt, attrlist=searchreq_attrlist, serverctrls=[req_ctrl] ) else: break else: print "Warning: Server ignores RFC 2696 control." break l.unbind_s() python-ldap-2.4.10/Demo/ms_ad_bind.py0000644000076400001440000000204311004050475020132 0ustar michaelusers00000000000000# How to bind to MS AD with python-ldap and various methods import ldap,ldap.sasl ldap_uri = "ldap://dc1.example.com" dn = "CN=Anna Blume,CN=Users,DC=addomain,DC=example,DC=com" sAMAccountName = "ABlume" userPrincipalName = "ablume@addomain.example.com" password = 'testsecret' trace_level = 2 l = ldap.initialize(ldap_uri,trace_level=trace_level) # Normal LDAPv3 compliant simple bind l.simple_bind_s(dn,password) # This is AD-specific and not LDAPv3 compliant l.simple_bind_s(userPrincipalName,password) # This is AD-specific and not LDAPv3 compliant l.simple_bind_s(userPrincipalName,password) # SASL bind with mech DIGEST-MD5 with sAMAccountName as SASL user name sasl_auth = ldap.sasl.sasl( { ldap.sasl.CB_AUTHNAME:sAMAccountName, ldap.sasl.CB_PASS :password, }, 'DIGEST-MD5' ) l.sasl_interactive_bind_s("", sasl_auth) # SASL bind with mech GSSAPI # with the help of Kerberos V TGT obtained before with command # kinit ablume@ADDOMAIN.EXAMPLE.COM sasl_auth = ldap.sasl.sasl({},'GSSAPI') l.sasl_interactive_bind_s("", sasl_auth) python-ldap-2.4.10/Demo/paged_search_ext_s.py0000644000076400001440000000571411704305351021675 0ustar michaelusers00000000000000url = "ldap://localhost:1390/" base = "dc=stroeder,dc=de" search_flt = r'(objectClass=*)' searchreq_attrlist=['cn','entryDN','entryUUID','mail','objectClass'] from ldap.ldapobject import ReconnectLDAPObject import ldap,pprint from ldap.controls import SimplePagedResultsControl class PagedResultsSearchObject: page_size = 50 def paged_search_ext_s(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0): """ Behaves exactly like LDAPObject.search_ext_s() but internally uses the simple paged results control to retrieve search results in chunks. This is non-sense for really large results sets which you would like to process one-by-one """ while True: # loop for reconnecting if necessary req_ctrl = SimplePagedResultsControl(True,size=self.page_size,cookie='') try: # Send first search request msgid = self.search_ext( base, scope, filterstr=filterstr, attrlist=attrlist, attrsonly=attrsonly, serverctrls=(serverctrls or [])+[req_ctrl], clientctrls=clientctrls, timeout=timeout, sizelimit=sizelimit ) result_pages = 0 all_results = [] while True: rtype, rdata, rmsgid, rctrls = self.result3(msgid) all_results.extend(rdata) result_pages += 1 # Extract the simple paged results response control pctrls = [ c for c in rctrls if c.controlType == SimplePagedResultsControl.controlType ] if pctrls: if pctrls[0].cookie: # Copy cookie from response control to request control req_ctrl.cookie = pctrls[0].cookie msgid = self.search_ext( base, scope, filterstr=filterstr, attrlist=attrlist, attrsonly=attrsonly, serverctrls=(serverctrls or [])+[req_ctrl], clientctrls=clientctrls, timeout=timeout, sizelimit=sizelimit ) else: break # no more pages available except ldap.SERVER_DOWN,e: try: self.reconnect(self._uri) except AttributeError: raise e else: return result_pages,all_results class MyLDAPObject(ReconnectLDAPObject,PagedResultsSearchObject): pass #ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) ldap.set_option(ldap.OPT_REFERRALS, 0) l = MyLDAPObject(url,trace_level=2,retry_max=100,retry_delay=2) l.protocol_version = 3 l.simple_bind_s("", "") l.page_size=10 # Send search request result_pages,all_results = l.paged_search_ext_s( base, ldap.SCOPE_SUBTREE, search_flt, attrlist=searchreq_attrlist, serverctrls=None ) l.unbind_s() print 'Received %d results in %d pages.' % (len(all_results),result_pages) python-ldap-2.4.10/Demo/options.py0000644000076400001440000000164107635354564017576 0ustar michaelusers00000000000000 import ldap host="localhost:1390" print "API info:",ldap.get_option(ldap.OPT_API_INFO) print "debug level:",ldap.get_option(ldap.OPT_DEBUG_LEVEL) #print "Setting debug level to 255..." #ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) #print "debug level:",ldap.get_option(ldap.OPT_DEBUG_LEVEL) print "default size limit:",ldap.get_option(ldap.OPT_SIZELIMIT) print "Setting default size limit to 10..." ldap.set_option(ldap.OPT_SIZELIMIT,10) print "default size limit:",ldap.get_option(ldap.OPT_SIZELIMIT) print "Creating connection to",host,"..." l=ldap.init(host) print "size limit:",l.get_option(ldap.OPT_SIZELIMIT) print "Setting connection size limit to 20..." l.set_option(ldap.OPT_SIZELIMIT,20) print "size limit:",l.get_option(ldap.OPT_SIZELIMIT) #print "Setting time limit to 60 secs..." l.set_option(ldap.OPT_TIMELIMIT,60) #print "time limit:",l.get_option(ldap.OPT_TIMELIMIT) print "Binding..." l.simple_bind_s("","") python-ldap-2.4.10/Demo/Lib/0000755000076400001440000000000011764172736016232 5ustar michaelusers00000000000000python-ldap-2.4.10/Demo/Lib/.cvsignore0000644000076400001440000000002507507672213020222 0ustar michaelusers00000000000000*.pyc *.pyo *.bck *~ python-ldap-2.4.10/Demo/Lib/ldap/0000755000076400001440000000000011764172736017152 5ustar michaelusers00000000000000python-ldap-2.4.10/Demo/Lib/ldap/.cvsignore0000644000076400001440000000002507507672213021142 0ustar michaelusers00000000000000*.pyc *.pyo *.bck *~ python-ldap-2.4.10/Demo/Lib/ldap/async/0000755000076400001440000000000011764172736020267 5ustar michaelusers00000000000000python-ldap-2.4.10/Demo/Lib/ldap/async/sizelimit.py0000644000076400001440000000204010411503653022626 0ustar michaelusers00000000000000""" ldifwriter - using ldap.async module for retrieving partial results in a list even though the exception ldap.SIZELIMIT_EXCEEDED was raised.output of LDIF stream Written by Michael Stroeder $Id: sizelimit.py,v 1.4 2006/03/26 12:23:07 stroeder Exp $ This example translates the naming context of data read from input, sanitizes some attributes, maps/removes object classes, maps/removes attributes., etc. It's far from being complete though. Python compability note: Tested on Python 2.0+, should run on Python 1.5.x. """ import sys,ldap,ldap.async s = ldap.async.List( ldap.initialize('ldap://localhost:1390'), ) s.startSearch( 'dc=stroeder,dc=de', ldap.SCOPE_SUBTREE, '(objectClass=*)', ) try: partial = s.processResults() except ldap.SIZELIMIT_EXCEEDED: sys.stderr.write('Warning: Server-side size limit exceeded.\n') else: if partial: sys.stderr.write('Warning: Only partial results received.\n') sys.stderr.write( '%d results received.\n' % ( len(s.allResults) ) ) python-ldap-2.4.10/Demo/Lib/ldap/async/.cvsignore0000644000076400001440000000002507520013005022236 0ustar michaelusers00000000000000*.pyc *.pyo *.bck *~ python-ldap-2.4.10/Demo/Lib/ldap/async/deltree.py0000644000076400001440000000511111235000725022240 0ustar michaelusers00000000000000import ldap,ldap.async class DeleteLeafs(ldap.async.AsyncSearchHandler): """ Class for deleting entries which are results of a search. DNs of Non-leaf entries are collected in DeleteLeafs.nonLeafEntries. """ _entryResultTypes = ldap.async._entryResultTypes def __init__(self,l): ldap.async.AsyncSearchHandler.__init__(self,l) self.nonLeafEntries = [] self.deletedEntries = 0 def startSearch(self,searchRoot,searchScope): if not searchScope in [ldap.SCOPE_ONELEVEL,ldap.SCOPE_SUBTREE]: raise ValueError, "Parameter searchScope must be either ldap.SCOPE_ONELEVEL or ldap.SCOPE_SUBTREE." self.nonLeafEntries = [] self.deletedEntries = 0 ldap.async.AsyncSearchHandler.startSearch( self, searchRoot, searchScope, filterStr='(objectClass=*)', attrList=['hasSubordinates','numSubordinates'], attrsOnly=0, ) def _processSingleResult(self,resultType,resultItem): if self._entryResultTypes.has_key(resultType): # Don't process search references dn,entry = resultItem hasSubordinates = entry.get( 'hasSubordinates', entry.get('hassubordinates',['FALSE'] ) )[0] numSubordinates = entry.get( 'numSubordinates', entry.get('numsubordinates',['0']) )[0] if hasSubordinates=='TRUE' or int(numSubordinates): self.nonLeafEntries.append(dn) else: try: self._l.delete_s(dn) except ldap.NOT_ALLOWED_ON_NONLEAF,e: self.nonLeafEntries.append(dn) else: self.deletedEntries = self.deletedEntries+1 def DelTree(l,dn,scope=ldap.SCOPE_ONELEVEL): """ Recursively delete entries below or including entry with name dn. """ leafs_deleter = DeleteLeafs(l) leafs_deleter.startSearch(dn,scope) leafs_deleter.processResults() deleted_entries = leafs_deleter.deletedEntries non_leaf_entries = leafs_deleter.nonLeafEntries[:] while non_leaf_entries: dn = non_leaf_entries.pop() print deleted_entries,len(non_leaf_entries),dn leafs_deleter.startSearch(dn,ldap.SCOPE_SUBTREE) leafs_deleter.processResults() deleted_entries = deleted_entries+leafs_deleter.deletedEntries non_leaf_entries.extend(leafs_deleter.nonLeafEntries) return # DelTree() # Create LDAPObject instance l = ldap.initialize('ldap://localhost:1390') # Try a bind to provoke failure if protocol version is not supported l.simple_bind_s('cn=Directory Manager,dc=IMC,dc=org','controller') # Delete all entries *below* the entry dc=Delete,dc=IMC,dc=org DelTree(l,'dc=Delete,dc=IMC,dc=org',ldap.SCOPE_ONELEVEL) python-ldap-2.4.10/Demo/Lib/ldap/async/ldifwriter.py0000644000076400001440000000176110411503653023001 0ustar michaelusers00000000000000""" ldifwriter - using ldap.async module for output of LDIF stream of LDAP search results Written by Michael Stroeder $Id: ldifwriter.py,v 1.4 2006/03/26 12:23:07 stroeder Exp $ This example translates the naming context of data read from input, sanitizes some attributes, maps/removes object classes, maps/removes attributes., etc. It's far from being complete though. Python compability note: Tested on Python 2.0+, should run on Python 1.5.x. """ import sys,ldap,ldap.async s = ldap.async.LDIFWriter( ldap.initialize('ldap://localhost:1390'), sys.stdout ) s.startSearch( 'dc=stroeder,dc=de', ldap.SCOPE_SUBTREE, '(objectClass=*)', ) try: partial = s.processResults() except ldap.SIZELIMIT_EXCEEDED: sys.stderr.write('Warning: Server-side size limit exceeded.\n') else: if partial: sys.stderr.write('Warning: Only partial results received.\n') sys.stderr.write( '%d results received.\n' % ( s.endResultBreak-s.beginResultsDropped ) ) python-ldap-2.4.10/Demo/Lib/ldif/0000755000076400001440000000000011764172736017150 5ustar michaelusers00000000000000python-ldap-2.4.10/Demo/Lib/ldif/.cvsignore0000644000076400001440000000002507507672213021140 0ustar michaelusers00000000000000*.pyc *.pyo *.bck *~ python-ldap-2.4.10/Demo/Lib/ldif/ldifcopy.py0000644000076400001440000000124207405752201021316 0ustar michaelusers00000000000000""" ldifcopy - reads LDIF from stdin, retrieve values by URL and write resulting LDIF to stdout Written by Michael Stroeder $Id: ldifcopy.py,v 1.2 2001/12/12 22:04:49 stroeder Exp $ This example translates the naming context of data read from input, sanitizes some attributes, maps/removes object classes, maps/removes attributes., etc. It's far from being complete though. Python compability note: Tested on Python 2.0+, should run on Python 1.5.x. """ import sys,ldif infile = sys.stdin outfile = sys.stdout ldif_collector = ldif.LDIFCopy( infile, outfile, process_url_schemes=['file','ftp','http'] ) ldif_collector.parse() python-ldap-2.4.10/Demo/Lib/ldapurl/0000755000076400001440000000000011764172736017675 5ustar michaelusers00000000000000python-ldap-2.4.10/Demo/Lib/ldapurl/urlsearch.py0000644000076400001440000000144207617316074022235 0ustar michaelusers00000000000000""" Do a search with the LDAP URL specified at command-line. No output of LDAP data is produced except trace output. """ import sys,getpass,ldap,ldapurl try: ldapUrl = ldapurl.LDAPUrl(ldapUrl=sys.argv[1]) except IndexError: print 'Usage: %s [LDAP URL]' % (sys.argv[0]) sys.exit(1) for a in [ 'urlscheme','hostport','dn','attrs','scope', 'filterstr','extensions','who','cred' ]: print a,repr(getattr(ldapUrl,a)) l = ldap.initialize(ldapUrl.initializeUrl(),trace_level=1) if ldapUrl.who!=None: if ldapUrl.cred!=None: cred=ldapUrl.cred else: print 'Enter password for simple bind with',repr(ldapUrl.who) cred=getpass.getpass() l.simple_bind_s(ldapUrl.who,cred) res = l.search_s(ldapUrl.dn,ldapUrl.scope,ldapUrl.filterstr,ldapUrl.attrs) print len(res),'search results' python-ldap-2.4.10/Demo/Lib/ldapurl/.cvsignore0000644000076400001440000000002507617316730021666 0ustar michaelusers00000000000000*.pyc *.pyo *.bck *~ python-ldap-2.4.10/Modules/0000755000076400001440000000000011764172736016250 5ustar michaelusers00000000000000python-ldap-2.4.10/Modules/LDAPObject.h0000644000076400001440000000312111172071675020256 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: LDAPObject.h,v 1.10 2009/04/17 12:19:09 stroeder Exp $ */ #ifndef __h_LDAPObject #define __h_LDAPObject #include "common.h" #include "lber.h" #include "ldap.h" #if LDAP_API_VERSION < 2000 #error Current python-ldap requires OpenLDAP 2.x #endif #if PYTHON_API_VERSION < 1007 typedef PyObject* _threadstate; #else typedef PyThreadState* _threadstate; #endif typedef struct { PyObject_HEAD LDAP* ldap; _threadstate _save; /* for thread saving on referrals */ int valid; } LDAPObject; extern PyTypeObject LDAP_Type; #define LDAPObject_Check(v) ((v)->ob_type == &LDAP_Type) extern LDAPObject *newLDAPObject( LDAP* ); /* macros to allow thread saving in the context of an LDAP connection */ #define LDAP_BEGIN_ALLOW_THREADS( l ) \ { \ LDAPObject *lo = (l); \ if (lo->_save != NULL) \ Py_FatalError( "saving thread twice?" ); \ lo->_save = PyEval_SaveThread(); \ } #define LDAP_END_ALLOW_THREADS( l ) \ { \ LDAPObject *lo = (l); \ _threadstate _save = lo->_save; \ lo->_save = NULL; \ PyEval_RestoreThread( _save ); \ } #endif /* __h_LDAPObject */ python-ldap-2.4.10/Modules/ldapcontrol.c0000644000076400001440000002056711660514462020736 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: ldapcontrol.c,v 1.20 2011/10/26 18:38:06 stroeder Exp $ */ #include "common.h" #include "LDAPObject.h" #include "ldapcontrol.h" #include "berval.h" #include "errors.h" #include "lber.h" /* Prints to stdout the contents of an array of LDAPControl objects */ /* XXX: This is a debugging tool, and the printf generates some warnings * about pointer types. I left it here in case something breaks and we * need to inspect an LDAPControl structure. static void LDAPControl_DumpList( LDAPControl** lcs ) { LDAPControl** lcp; LDAPControl* lc; for ( lcp = lcs; *lcp; lcp++ ) { lc = *lcp; printf("OID: %s\nCriticality: %d\nBER length: %d\nBER value: %x\n", lc->ldctl_oid, lc->ldctl_iscritical, lc->ldctl_value.bv_len, lc->ldctl_value.bv_val); } } */ /* Free a single LDAPControl object created by Tuple_to_LDAPControl */ static void LDAPControl_DEL( LDAPControl* lc ) { if (lc == NULL) return; if (lc->ldctl_oid) PyMem_DEL(lc->ldctl_oid); PyMem_DEL(lc); } /* Free an array of LDAPControl objects created by LDAPControls_from_object */ void LDAPControl_List_DEL( LDAPControl** lcs ) { LDAPControl** lcp; if (lcs == NULL) return; for ( lcp = lcs; *lcp; lcp++ ) LDAPControl_DEL( *lcp ); PyMem_DEL( lcs ); } /* Takes a tuple of the form: * (OID: string, Criticality: int/boolean, Value: string/None) * and converts it into an LDAPControl structure. * * The Value string should represent an ASN.1 encoded structure. */ static LDAPControl* Tuple_to_LDAPControl( PyObject* tup ) { char *oid; char iscritical; struct berval berbytes; PyObject *bytes; LDAPControl *lc = NULL; Py_ssize_t len; if (!PyTuple_Check(tup)) { PyErr_SetObject(PyExc_TypeError, Py_BuildValue("sO", "expected a tuple", tup)); return NULL; } if (!PyArg_ParseTuple( tup, "sbO", &oid, &iscritical, &bytes )) return NULL; lc = PyMem_NEW(LDAPControl, 1); if (lc == NULL) { PyErr_NoMemory(); return NULL; } lc->ldctl_iscritical = iscritical; len = strlen(oid); lc->ldctl_oid = PyMem_NEW(char, len + 1); if (lc->ldctl_oid == NULL) { PyErr_NoMemory(); LDAPControl_DEL(lc); return NULL; } memcpy(lc->ldctl_oid, oid, len + 1); /* The berval can either be None or a String */ if (PyNone_Check(bytes)) { berbytes.bv_len = 0; berbytes.bv_val = NULL; } else if (PyString_Check(bytes)) { berbytes.bv_len = PyString_Size(bytes); berbytes.bv_val = PyString_AsString(bytes); } else { PyErr_SetObject(PyExc_TypeError, Py_BuildValue("sO", "expected a string", bytes)); LDAPControl_DEL(lc); return NULL; } lc->ldctl_value = berbytes; return lc; } /* Convert a list of tuples (of a format acceptable to the Tuple_to_LDAPControl * function) into an array of LDAPControl objects. */ int LDAPControls_from_object(PyObject* list, LDAPControl ***controls_ret) { Py_ssize_t len, i; LDAPControl** ldcs; LDAPControl* ldc; PyObject* item; if (!PySequence_Check(list)) { PyErr_SetObject(PyExc_TypeError, Py_BuildValue("sO", "expected a list", list)); return 0; } len = PySequence_Length(list); ldcs = PyMem_NEW(LDAPControl*, len + 1); if (ldcs == NULL) { PyErr_NoMemory(); return 0; } for (i = 0; i < len; i++) { item = PySequence_GetItem(list, i); if (item == NULL) { PyMem_DEL(ldcs); return 0; } ldc = Tuple_to_LDAPControl(item); if (ldc == NULL) { Py_DECREF(item); PyMem_DEL(ldcs); return 0; } ldcs[i] = ldc; Py_DECREF(item); } ldcs[len] = NULL; *controls_ret = ldcs; return 1; } PyObject* LDAPControls_to_List(LDAPControl **ldcs) { PyObject *res = 0, *pyctrl; LDAPControl **tmp = ldcs; Py_ssize_t num_ctrls = 0, i; if (tmp) while (*tmp++) num_ctrls++; if (!(res = PyList_New(num_ctrls))) goto endlbl; for (i = 0; i < num_ctrls; i++) { if (!(pyctrl = Py_BuildValue("sbO&", ldcs[i]->ldctl_oid, ldcs[i]->ldctl_iscritical, LDAPberval_to_object, &ldcs[i]->ldctl_value))) { goto endlbl; } PyList_SET_ITEM(res, i, pyctrl); } Py_INCREF(res); endlbl: Py_XDECREF(res); return res; } /* --------------- en-/decoders ------------- */ /* Matched Values, aka, Values Return Filter */ static PyObject* encode_rfc3876(PyObject *self, PyObject *args) { PyObject *res = 0; int err; BerElement *vrber = 0; char *vrFilter; struct berval *ctrl_val; if (!PyArg_ParseTuple(args, "s:encode_valuesreturnfilter_control", &vrFilter)) { goto endlbl; } if (!(vrber = ber_alloc_t(LBER_USE_DER))) { LDAPerr(LDAP_NO_MEMORY); goto endlbl; } err = ldap_put_vrFilter(vrber, vrFilter); if (err == -1) { LDAPerr(LDAP_FILTER_ERROR); goto endlbl; } err = ber_flatten(vrber, &ctrl_val); if (err == -1) { LDAPerr(LDAP_NO_MEMORY); goto endlbl; } res = LDAPberval_to_object(ctrl_val); endlbl: if (vrber) ber_free(vrber, 1); return res; } static PyObject* encode_rfc2696(PyObject *self, PyObject *args) { PyObject *res = 0; BerElement *ber = 0; struct berval cookie, *ctrl_val; Py_ssize_t cookie_len; unsigned long size; ber_tag_t tag; if (!PyArg_ParseTuple(args, "is#:encode_page_control", &size, &cookie.bv_val, &cookie_len)) { goto endlbl; } cookie.bv_len = (ber_len_t) cookie_len; if (!(ber = ber_alloc_t(LBER_USE_DER))) { LDAPerr(LDAP_NO_MEMORY); goto endlbl; } tag = ber_printf(ber, "{i", size); if (tag == LBER_ERROR) { LDAPerr(LDAP_ENCODING_ERROR); goto endlbl; } if (!cookie.bv_len) tag = ber_printf(ber, "o", "", 0); else tag = ber_printf(ber, "O", &cookie); if (tag == LBER_ERROR) { LDAPerr(LDAP_ENCODING_ERROR); goto endlbl; } tag = ber_printf(ber, /*{ */ "N}"); if (tag == LBER_ERROR) { LDAPerr(LDAP_ENCODING_ERROR); goto endlbl; } if (-1 == ber_flatten(ber, &ctrl_val)) { LDAPerr(LDAP_NO_MEMORY); goto endlbl; } res = LDAPberval_to_object(ctrl_val); endlbl: if (ber) ber_free(ber, 1); return res; } static PyObject* decode_rfc2696(PyObject *self, PyObject *args) { PyObject *res = 0; BerElement *ber = 0; struct berval ldctl_value; ber_tag_t tag; struct berval *cookiep; unsigned long count; Py_ssize_t ldctl_value_len; if (!PyArg_ParseTuple(args, "s#:decode_page_control", &ldctl_value.bv_val, &ldctl_value_len)) { goto endlbl; } ldctl_value.bv_len = (ber_len_t) ldctl_value_len; if (!(ber = ber_init(&ldctl_value))) { LDAPerr(LDAP_NO_MEMORY); goto endlbl; } tag = ber_scanf(ber, "{iO", &count, &cookiep); if (tag == LBER_ERROR) { LDAPerr(LDAP_DECODING_ERROR); goto endlbl; } res = Py_BuildValue("(lO&)", count, LDAPberval_to_object, cookiep); endlbl: if (ber) ber_free(ber, 1); return res; } static PyObject* encode_assertion_control(PyObject *self, PyObject *args) { int err; PyObject *res = 0; char *assertion_filterstr; struct berval ctrl_val; LDAP *ld = NULL; if (!PyArg_ParseTuple(args, "s:encode_assertion_control", &assertion_filterstr)) { goto endlbl; } err = ldap_create(&ld); if (err != LDAP_SUCCESS) return LDAPerror(ld, "ldap_create"); err = ldap_create_assertion_control_value(ld,assertion_filterstr,&ctrl_val); if (err != LDAP_SUCCESS) return LDAPerror(ld, "ldap_create_assertion_control_value"); res = LDAPberval_to_object(&ctrl_val); endlbl: return res; } static PyMethodDef methods[] = { {"encode_page_control", encode_rfc2696, METH_VARARGS }, {"decode_page_control", decode_rfc2696, METH_VARARGS }, {"encode_valuesreturnfilter_control", encode_rfc3876, METH_VARARGS }, {"encode_assertion_control", encode_assertion_control, METH_VARARGS }, { NULL, NULL } }; void LDAPinit_control(PyObject *d) { LDAPadd_methods(d, methods); } python-ldap-2.4.10/Modules/ldapmodule.c0000644000076400001440000000171411172071675020536 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: ldapmodule.c,v 1.9 2009/04/17 12:19:09 stroeder Exp $ */ #include "common.h" #include "version.h" #include "constants.h" #include "errors.h" #include "functions.h" #include "schema.h" #include "ldapcontrol.h" #include "LDAPObject.h" DL_EXPORT(void) init_ldap(void); /* dummy module methods */ static PyMethodDef methods[] = { { NULL, NULL } }; /* module initialisation */ DL_EXPORT(void) init_ldap() { PyObject *m, *d; #if defined(MS_WINDOWS) || defined(__CYGWIN__) LDAP_Type.ob_type = &PyType_Type; #endif /* Create the module and add the functions */ m = Py_InitModule("_ldap", methods); /* Add some symbolic constants to the module */ d = PyModule_GetDict(m); LDAPinit_version(d); LDAPinit_constants(d); LDAPinit_errors(d); LDAPinit_functions(d); LDAPinit_schema(d); LDAPinit_control(d); /* Check for errors */ if (PyErr_Occurred()) Py_FatalError("can't initialize module _ldap"); } python-ldap-2.4.10/Modules/common.c0000644000076400001440000000076411172071675017704 0ustar michaelusers00000000000000/* Miscellaneous common routines * See http://www.python-ldap.org/ for details. * $Id: common.c,v 1.3 2009/04/17 12:19:09 stroeder Exp $ */ #include "common.h" /* dynamically add the methods into the module dictionary d */ void LDAPadd_methods( PyObject* d, PyMethodDef* methods ) { PyMethodDef *meth; for( meth = methods; meth->ml_meth; meth++ ) { PyObject *f = PyCFunction_New( meth, NULL ); PyDict_SetItemString( d, meth->ml_name, f ); Py_DECREF(f); } } python-ldap-2.4.10/Modules/.cvsignore0000644000076400001440000000001107507671633020240 0ustar michaelusers00000000000000*.bck *~ python-ldap-2.4.10/Modules/ldapcontrol.h0000644000076400001440000000065011235744576020743 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: ldapcontrol.h,v 1.6 2009/08/04 05:39:10 leonard Exp $ */ #ifndef __h_ldapcontrol #define __h_ldapcontrol #include "common.h" #include "ldap.h" void LDAPinit_control(PyObject *d); void LDAPControl_List_DEL( LDAPControl** ); int LDAPControls_from_object(PyObject *, LDAPControl ***); PyObject* LDAPControls_to_List(LDAPControl **ldcs); #endif /* __h_ldapcontrol */ python-ldap-2.4.10/Modules/version.c0000644000076400001440000000066111172071675020075 0ustar michaelusers00000000000000/* Set release version * See http://www.python-ldap.org/ for details. * $Id: version.c,v 1.4 2009/04/17 12:19:09 stroeder Exp $ */ #include "common.h" #define _STR(x) #x #define STR(x) _STR(x) static char version_str[] = STR(LDAPMODULE_VERSION); void LDAPinit_version( PyObject* d ) { PyObject *version; version = PyString_FromString(version_str); PyDict_SetItemString( d, "__version__", version ); Py_DECREF(version); } python-ldap-2.4.10/Modules/schema.c0000644000076400001440000002027611172071675017654 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: schema.c,v 1.8 2009/04/17 12:19:09 stroeder Exp $ */ #include "common.h" #include "schema.h" #include "ldap_schema.h" /* This utility function takes a null delimited C array of (null delimited) C strings, creates its python equivalent and returns a new reference to it. If the array is empty or the pointer to it is NULL, an empty python array is returned. */ PyObject* c_string_array_to_python(char **string_array) { Py_ssize_t count = 0; char **s; PyObject *py_list; if (string_array) { for (s=string_array; *s != 0; s++) count++; py_list = PyList_New(count); count = 0; for (s=string_array; *s != 0; s++){ PyList_SetItem(py_list, count, PyString_FromString(*s)); count++; } } else py_list=PyList_New(0); return py_list; } /* This function returns a list of tuples. The first entry of each tuple is a string (lsei_name), and the second is a lists built from lsei_values. Probably the C data structure is modeled along the lines of a mapping "lsei_name -> (list of lsei_values)". However, there seems to be no guarantee that a lsei_name is unique, so I dare not use a python mapping for this beast... */ PyObject* schema_extension_to_python(LDAPSchemaExtensionItem **extensions) { Py_ssize_t count = 0; LDAPSchemaExtensionItem **e; PyObject *py_list, *item_tuple; if (extensions) { for (e = extensions; *e !=0; e++) count++; py_list = PyList_New(count); count = 0; for (e = extensions; *e !=0; e++) { item_tuple = PyTuple_New(2); PyTuple_SetItem(item_tuple, 0, PyString_FromString((*e)->lsei_name)); PyTuple_SetItem(item_tuple, 1, c_string_array_to_python((*e)->lsei_values)); PyList_SetItem(py_list, count, item_tuple); count++; } } else py_list=PyList_New(0); return py_list; } /* The following four functions do the boring job: they take a python string, feed it into the respective parser functions provided by openldap, and build a python list from the data structure returned by the C function. */ static char doc_ldap_str2objectclass[] = ""; static PyObject* l_ldap_str2objectclass(PyObject* self, PyObject *args) { int ret=0, flag = LDAP_SCHEMA_ALLOW_NONE; char *oc_string; const char *errp; LDAPObjectClass *o; PyObject *oc_names, *oc_sup_oids, *oc_at_oids_must, *oc_at_oids_may, *py_ret; if (!PyArg_ParseTuple(args, "si", &oc_string, &flag)) return NULL; o = ldap_str2objectclass( oc_string, &ret, &errp, flag); if (ret) { py_ret = PyInt_FromLong(ret); return py_ret; } oc_sup_oids = c_string_array_to_python(o->oc_sup_oids); oc_names = c_string_array_to_python(o->oc_names); oc_at_oids_must = c_string_array_to_python(o->oc_at_oids_must); oc_at_oids_may = c_string_array_to_python(o->oc_at_oids_may); py_ret = PyList_New(9); PyList_SetItem(py_ret, 0, PyString_FromString(o->oc_oid)); PyList_SetItem(py_ret, 1, oc_names); if (o->oc_desc) { PyList_SetItem(py_ret, 2, PyString_FromString(o->oc_desc)); } else { PyList_SetItem(py_ret, 2, PyString_FromString("")); } PyList_SetItem(py_ret, 3, PyInt_FromLong(o->oc_obsolete)); PyList_SetItem(py_ret, 4, oc_sup_oids); PyList_SetItem(py_ret, 5, PyInt_FromLong(o->oc_kind)); PyList_SetItem(py_ret, 6, oc_at_oids_must); PyList_SetItem(py_ret, 7, oc_at_oids_may); PyList_SetItem(py_ret, 8, schema_extension_to_python(o->oc_extensions)); ldap_objectclass_free(o); return py_ret; } static char doc_ldap_str2attributetype[] = ""; static PyObject* l_ldap_str2attributetype(PyObject* self, PyObject *args) { int ret=0, flag = LDAP_SCHEMA_ALLOW_NONE; char *at_string; const char *errp; LDAPAttributeType *a; PyObject *py_ret; PyObject *at_names; if (!PyArg_ParseTuple(args, "si", &at_string,&flag)) return NULL; a = ldap_str2attributetype( at_string, &ret, &errp, flag); if (ret) { py_ret = PyInt_FromLong(ret); return py_ret; } py_ret = PyList_New(15); PyList_SetItem(py_ret, 0, PyString_FromString(a->at_oid)); at_names = c_string_array_to_python(a->at_names); PyList_SetItem(py_ret, 1, at_names); if (a->at_desc) { PyList_SetItem(py_ret, 2, PyString_FromString(a->at_desc)); } else { PyList_SetItem(py_ret, 2, PyString_FromString("")); } PyList_SetItem(py_ret, 3, PyInt_FromLong(a->at_obsolete)); if (a->at_sup_oid) { PyList_SetItem(py_ret, 4, PyString_FromString(a->at_sup_oid)); } else { PyList_SetItem(py_ret, 4, PyString_FromString("")); } if (a->at_equality_oid) { PyList_SetItem(py_ret, 5, PyString_FromString(a->at_equality_oid)); } else { PyList_SetItem(py_ret, 5, PyString_FromString("")); } if (a->at_ordering_oid) { PyList_SetItem(py_ret, 6, PyString_FromString(a->at_ordering_oid)); } else { PyList_SetItem(py_ret, 6, PyString_FromString("")); } if (a->at_substr_oid) { PyList_SetItem(py_ret, 7, PyString_FromString(a->at_substr_oid)); } else { PyList_SetItem(py_ret, 7, PyString_FromString("")); } if (a->at_syntax_oid) { PyList_SetItem(py_ret, 8, PyString_FromString(a->at_syntax_oid)); } else { PyList_SetItem(py_ret, 8, PyString_FromString("")); } PyList_SetItem(py_ret, 9, PyInt_FromLong(a->at_syntax_len)); PyList_SetItem(py_ret,10, PyInt_FromLong(a->at_single_value)); PyList_SetItem(py_ret,11, PyInt_FromLong(a->at_collective)); PyList_SetItem(py_ret,12, PyInt_FromLong(a->at_no_user_mod)); PyList_SetItem(py_ret,13, PyInt_FromLong(a->at_usage)); PyList_SetItem(py_ret, 14, schema_extension_to_python(a->at_extensions)); ldap_attributetype_free(a); return py_ret; } static char doc_ldap_str2syntax[] = ""; static PyObject* l_ldap_str2syntax(PyObject* self, PyObject *args) { LDAPSyntax *s; int ret=0, flag = LDAP_SCHEMA_ALLOW_NONE; const char *errp; char *syn_string; PyObject *py_ret, *syn_names; if (!PyArg_ParseTuple(args, "si", &syn_string,&flag)) return NULL; s = ldap_str2syntax(syn_string, &ret, &errp, flag); if (ret) { py_ret = PyInt_FromLong(ret); return py_ret; } py_ret = PyList_New(4); PyList_SetItem(py_ret, 0, PyString_FromString(s->syn_oid)); syn_names = c_string_array_to_python(s->syn_names); PyList_SetItem(py_ret, 1, syn_names); if (s->syn_desc) { PyList_SetItem(py_ret, 2, PyString_FromString(s->syn_desc)); } else { PyList_SetItem(py_ret, 2, PyString_FromString("")); } PyList_SetItem(py_ret, 3, schema_extension_to_python(s->syn_extensions)); ldap_syntax_free(s); return py_ret; } static char doc_ldap_str2matchingrule[] = ""; static PyObject* l_ldap_str2matchingrule(PyObject* self, PyObject *args) { LDAPMatchingRule *m; int ret=0, flag = LDAP_SCHEMA_ALLOW_NONE; const char *errp; char *mr_string; PyObject *py_ret, *mr_names; if (!PyArg_ParseTuple(args, "si", &mr_string,&flag)) return NULL; m = ldap_str2matchingrule(mr_string, &ret, &errp, flag); if (ret) { py_ret = PyInt_FromLong(ret); return py_ret; } py_ret = PyList_New(6); PyList_SetItem(py_ret, 0, PyString_FromString(m->mr_oid)); mr_names = c_string_array_to_python(m->mr_names); PyList_SetItem(py_ret, 1, mr_names); if (m->mr_desc) { PyList_SetItem(py_ret, 2, PyString_FromString(m->mr_desc)); } else { PyList_SetItem(py_ret, 2, PyString_FromString("")); } PyList_SetItem(py_ret, 3, PyInt_FromLong(m->mr_obsolete)); if (m->mr_syntax_oid) { PyList_SetItem(py_ret, 4, PyString_FromString(m->mr_syntax_oid)); } else { PyList_SetItem(py_ret, 4, PyString_FromString("")); } PyList_SetItem(py_ret, 5, schema_extension_to_python(m->mr_extensions)); ldap_matchingrule_free(m); return py_ret; } /* methods */ static PyMethodDef methods[] = { { "str2objectclass", (PyCFunction)l_ldap_str2objectclass, METH_VARARGS, doc_ldap_str2objectclass }, { "str2attributetype", (PyCFunction)l_ldap_str2attributetype, METH_VARARGS, doc_ldap_str2attributetype }, { "str2syntax", (PyCFunction)l_ldap_str2syntax, METH_VARARGS, doc_ldap_str2syntax }, { "str2matchingrule", (PyCFunction)l_ldap_str2matchingrule, METH_VARARGS, doc_ldap_str2matchingrule }, { NULL, NULL } }; void LDAPinit_schema( PyObject* d ) { LDAPadd_methods( d, methods ); } python-ldap-2.4.10/Modules/schema.h0000644000076400001440000000036311172071675017654 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: schema.h,v 1.5 2009/04/17 12:19:09 stroeder Exp $ */ #ifndef __h_schema_ #define __h_schema_ #include "common.h" extern void LDAPinit_schema( PyObject* ); #endif /* __h_schema_ */ python-ldap-2.4.10/Modules/constants.c0000644000076400001440000002207611660514462020426 0ustar michaelusers00000000000000/* constants defined for LDAP * See http://www.python-ldap.org/ for details. * $Id: constants.c,v 1.55 2011/10/14 11:47:08 stroeder Exp $ */ #include "common.h" #include "constants.h" #include "lber.h" #include "ldap.h" static PyObject* reverse; static PyObject* forward; /* convert an result integer into a Python string */ PyObject* LDAPconstant( int val ) { PyObject *i = PyInt_FromLong( val ); PyObject *s = PyObject_GetItem( reverse, i ); if (s == NULL) { PyErr_Clear(); return i; } Py_DECREF(i); return s; } /* initialise the module constants */ void LDAPinit_constants( PyObject* d ) { PyObject *zero, *author,*obj; reverse = PyDict_New(); forward = PyDict_New(); PyDict_SetItemString( d, "_reverse", reverse ); PyDict_SetItemString( d, "_forward", forward ); #define add_int(d, name) \ { \ PyObject *i = PyInt_FromLong(LDAP_##name); \ PyDict_SetItemString( d, #name, i ); \ Py_DECREF(i); \ } /* simple constants */ add_int(d,API_VERSION); add_int(d,VENDOR_VERSION); add_int(d,PORT); add_int(d,VERSION1); add_int(d,VERSION2); add_int(d,VERSION3); add_int(d,VERSION_MIN); add_int(d,VERSION); add_int(d,VERSION_MAX); add_int(d,TAG_MESSAGE); add_int(d,TAG_MSGID); add_int(d,REQ_BIND); add_int(d,REQ_UNBIND); add_int(d,REQ_SEARCH); add_int(d,REQ_MODIFY); add_int(d,REQ_ADD); add_int(d,REQ_DELETE); add_int(d,REQ_MODRDN); add_int(d,REQ_COMPARE); add_int(d,REQ_ABANDON); add_int(d,TAG_LDAPDN); add_int(d,TAG_LDAPCRED); add_int(d,TAG_CONTROLS); add_int(d,TAG_REFERRAL); add_int(d,REQ_EXTENDED); #if LDAP_API_VERSION >= 2004 add_int(d,TAG_NEWSUPERIOR); add_int(d,TAG_EXOP_REQ_OID); add_int(d,TAG_EXOP_REQ_VALUE); add_int(d,TAG_EXOP_RES_OID); add_int(d,TAG_EXOP_RES_VALUE); #ifdef HAVE_SASL add_int(d,TAG_SASL_RES_CREDS); #endif #endif add_int(d,SASL_AUTOMATIC); add_int(d,SASL_INTERACTIVE); add_int(d,SASL_QUIET); /* reversibles */ zero = PyInt_FromLong( 0 ); PyDict_SetItem( reverse, zero, Py_None ); Py_DECREF( zero ); add_int(d,RES_BIND); add_int(d,RES_SEARCH_ENTRY); add_int(d,RES_SEARCH_RESULT); add_int(d,RES_MODIFY); add_int(d,RES_ADD); add_int(d,RES_DELETE); add_int(d,RES_MODRDN); add_int(d,RES_COMPARE); add_int(d,RES_ANY); add_int(d,RES_SEARCH_REFERENCE); add_int(d,RES_EXTENDED); add_int(d,RES_UNSOLICITED); add_int(d,RES_INTERMEDIATE); /* non-reversibles */ add_int(d,AUTH_NONE); add_int(d,AUTH_SIMPLE); add_int(d,SCOPE_BASE); add_int(d,SCOPE_ONELEVEL); add_int(d,SCOPE_SUBTREE); add_int(d,MOD_ADD); add_int(d,MOD_DELETE); add_int(d,MOD_REPLACE); add_int(d,MOD_INCREMENT); add_int(d,MOD_BVALUES); add_int(d,MSG_ONE); add_int(d,MSG_ALL); add_int(d,MSG_RECEIVED); /* (errors.c contains the error constants) */ add_int(d,DEREF_NEVER); add_int(d,DEREF_SEARCHING); add_int(d,DEREF_FINDING); add_int(d,DEREF_ALWAYS); add_int(d,NO_LIMIT); add_int(d,OPT_API_INFO); add_int(d,OPT_DEREF); add_int(d,OPT_SIZELIMIT); add_int(d,OPT_TIMELIMIT); #ifdef LDAP_OPT_REFERRALS add_int(d,OPT_REFERRALS); #endif add_int(d,OPT_ERROR_NUMBER); add_int(d,OPT_RESTART); add_int(d,OPT_PROTOCOL_VERSION); add_int(d,OPT_SERVER_CONTROLS); add_int(d,OPT_CLIENT_CONTROLS); add_int(d,OPT_API_FEATURE_INFO); add_int(d,OPT_HOST_NAME); add_int(d,OPT_DIAGNOSTIC_MESSAGE); add_int(d,OPT_ERROR_STRING); add_int(d,OPT_MATCHED_DN); add_int(d,OPT_DEBUG_LEVEL); add_int(d,OPT_TIMEOUT); add_int(d,OPT_REFHOPLIMIT); add_int(d,OPT_NETWORK_TIMEOUT); add_int(d,OPT_URI); #ifdef LDAP_OPT_DEFBASE add_int(d,OPT_DEFBASE); #endif #ifdef HAVE_TLS add_int(d,OPT_X_TLS); #ifdef LDAP_OPT_X_TLS_NEWCTX add_int(d,OPT_X_TLS_CTX); #endif add_int(d,OPT_X_TLS_CACERTFILE); add_int(d,OPT_X_TLS_CACERTDIR); add_int(d,OPT_X_TLS_CERTFILE); add_int(d,OPT_X_TLS_KEYFILE); add_int(d,OPT_X_TLS_REQUIRE_CERT); add_int(d,OPT_X_TLS_CIPHER_SUITE); add_int(d,OPT_X_TLS_RANDOM_FILE); add_int(d,OPT_X_TLS_DHFILE); add_int(d,OPT_X_TLS_NEVER); add_int(d,OPT_X_TLS_HARD); add_int(d,OPT_X_TLS_DEMAND); add_int(d,OPT_X_TLS_ALLOW); add_int(d,OPT_X_TLS_TRY); #ifdef LDAP_OPT_X_TLS_CRLCHECK /* only available if OpenSSL supports it => might cause backward compability problems */ add_int(d,OPT_X_TLS_CRLCHECK); #ifdef LDAP_OPT_X_TLS_CRLFILE add_int(d,OPT_X_TLS_CRLFILE); #endif add_int(d,OPT_X_TLS_CRL_NONE); add_int(d,OPT_X_TLS_CRL_PEER); add_int(d,OPT_X_TLS_CRL_ALL); #endif #ifdef LDAP_OPT_X_TLS_NEWCTX add_int(d,OPT_X_TLS_NEWCTX); #endif #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN add_int(d,OPT_X_TLS_PROTOCOL_MIN); #endif #ifdef LDAP_OPT_X_TLS_PACKAGE add_int(d,OPT_X_TLS_PACKAGE); #endif #endif add_int(d,OPT_X_SASL_MECH); add_int(d,OPT_X_SASL_REALM); add_int(d,OPT_X_SASL_AUTHCID); add_int(d,OPT_X_SASL_AUTHZID); add_int(d,OPT_X_SASL_SSF); add_int(d,OPT_X_SASL_SSF_EXTERNAL); add_int(d,OPT_X_SASL_SECPROPS); add_int(d,OPT_X_SASL_SSF_MIN); add_int(d,OPT_X_SASL_SSF_MAX); #ifdef LDAP_OPT_X_SASL_NOCANON add_int(d,OPT_X_SASL_NOCANON); #endif #ifdef LDAP_OPT_X_SASL_USERNAME add_int(d,OPT_X_SASL_USERNAME); #endif #ifdef LDAP_OPT_CONNECT_ASYNC add_int(d,OPT_CONNECT_ASYNC); #endif #ifdef LDAP_OPT_X_KEEPALIVE_IDLE add_int(d,OPT_X_KEEPALIVE_IDLE); #endif #ifdef LDAP_OPT_X_KEEPALIVE_PROBES add_int(d,OPT_X_KEEPALIVE_PROBES); #endif #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL add_int(d,OPT_X_KEEPALIVE_INTERVAL); #endif add_int(d,DN_FORMAT_LDAP); add_int(d,DN_FORMAT_LDAPV3); add_int(d,DN_FORMAT_LDAPV2); add_int(d,DN_FORMAT_DCE); add_int(d,DN_FORMAT_UFN); add_int(d,DN_FORMAT_AD_CANONICAL); /* add_int(d,DN_FORMAT_LBER); */ /* "for testing only" */ add_int(d,DN_FORMAT_MASK); add_int(d,DN_PRETTY); add_int(d,DN_SKIP); add_int(d,DN_P_NOLEADTRAILSPACES); add_int(d,DN_P_NOSPACEAFTERRDN); add_int(d,DN_PEDANTIC); add_int(d,AVA_NULL); add_int(d,AVA_STRING); add_int(d,AVA_BINARY); add_int(d,AVA_NONPRINTABLE); /*add_int(d,OPT_ON);*/ obj = PyInt_FromLong(1); PyDict_SetItemString( d, "OPT_ON", obj ); Py_DECREF(obj); /*add_int(d,OPT_OFF);*/ obj = PyInt_FromLong(0); PyDict_SetItemString( d, "OPT_OFF", obj ); Py_DECREF(obj); add_int(d,OPT_SUCCESS); /* XXX - these belong in errors.c */ add_int(d,URL_ERR_BADSCOPE); add_int(d,URL_ERR_MEM); /* author */ author = PyString_FromString("python-ldap Project"); PyDict_SetItemString(d, "__author__", author); Py_DECREF(author); /* add_int(d,LIBLDAP_R); */ #ifdef HAVE_LIBLDAP_R obj = PyInt_FromLong(1); #else obj = PyInt_FromLong(0); #endif PyDict_SetItemString( d, "LIBLDAP_R", obj ); Py_DECREF(obj); /* add_int(d,SASL); */ #ifdef HAVE_SASL obj = PyInt_FromLong(1); #else obj = PyInt_FromLong(0); #endif PyDict_SetItemString( d, "SASL_AVAIL", obj ); Py_DECREF(obj); /* add_int(d,TLS); */ #ifdef HAVE_TLS obj = PyInt_FromLong(1); #else obj = PyInt_FromLong(0); #endif PyDict_SetItemString( d, "TLS_AVAIL", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_MANAGEDSAIT); PyDict_SetItemString( d, "CONTROL_MANAGEDSAIT", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_PROXY_AUTHZ); PyDict_SetItemString( d, "CONTROL_PROXY_AUTHZ", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_SUBENTRIES); PyDict_SetItemString( d, "CONTROL_SUBENTRIES", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_VALUESRETURNFILTER); PyDict_SetItemString( d, "CONTROL_VALUESRETURNFILTER", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_ASSERT); PyDict_SetItemString( d, "CONTROL_ASSERT", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_PRE_READ); PyDict_SetItemString( d, "CONTROL_PRE_READ", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_POST_READ); PyDict_SetItemString( d, "CONTROL_POST_READ", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_SORTREQUEST); PyDict_SetItemString( d, "CONTROL_SORTREQUEST", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_SORTRESPONSE); PyDict_SetItemString( d, "CONTROL_SORTRESPONSE", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_PAGEDRESULTS); PyDict_SetItemString( d, "CONTROL_PAGEDRESULTS", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_SYNC); PyDict_SetItemString( d, "CONTROL_SYNC", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_SYNC_STATE); PyDict_SetItemString( d, "CONTROL_SYNC_STATE", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_SYNC_DONE); PyDict_SetItemString( d, "CONTROL_SYNC_DONE", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_SYNC_INFO); PyDict_SetItemString( d, "SYNC_INFO", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_PASSWORDPOLICYREQUEST); PyDict_SetItemString( d, "CONTROL_PASSWORDPOLICYREQUEST", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_PASSWORDPOLICYRESPONSE); PyDict_SetItemString( d, "CONTROL_PASSWORDPOLICYRESPONSE", obj ); Py_DECREF(obj); obj = PyString_FromString(LDAP_CONTROL_RELAX); PyDict_SetItemString( d, "CONTROL_RELAX", obj ); Py_DECREF(obj); } python-ldap-2.4.10/Modules/functions.h0000644000076400001440000000047711172071675020432 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: functions.h,v 1.4 2009/04/17 12:19:09 stroeder Exp $ */ #ifndef __h_functions_ #define __h_functions_ /* $Id: functions.h,v 1.4 2009/04/17 12:19:09 stroeder Exp $ */ #include "common.h" extern void LDAPinit_functions( PyObject* ); #endif /* __h_functions_ */ python-ldap-2.4.10/Modules/common.h0000644000076400001440000000143211172071675017702 0ustar michaelusers00000000000000/* common utility macros * See http://www.python-ldap.org/ for details. * $Id: common.h,v 1.8 2009/04/17 12:19:09 stroeder Exp $ */ #ifndef __h_common #define __h_common #define PY_SSIZE_T_CLEAN #include "Python.h" #if defined(HAVE_CONFIG_H) #include "config.h" #endif #if defined(MS_WINDOWS) #include #else /* unix */ #include #include #include #endif /* Backwards compability with Python prior 2.5 */ #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #endif #include #define streq( a, b ) \ ( (*(a)==*(b)) && 0==strcmp(a,b) ) void LDAPadd_methods( PyObject*d, PyMethodDef*methods ); #define PyNone_Check(o) ((o) == Py_None) #endif /* __h_common_ */ python-ldap-2.4.10/Modules/message.h0000644000076400001440000000052711530551500020026 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: message.h,v 1.6 2011/02/21 21:04:00 stroeder Exp $ */ #ifndef __h_message #define __h_message #include "common.h" #include "lber.h" #include "ldap.h" extern PyObject* LDAPmessage_to_python( LDAP*ld, LDAPMessage*m, int add_ctrls, int add_intermediates ); #endif /* __h_message_ */ python-ldap-2.4.10/Modules/berval.h0000644000076400001440000000065511242133473017664 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: berval.h,v 1.1 2009/08/17 01:49:47 leonard Exp $ */ #ifndef __h_berval #define __h_berval #include "common.h" #include "lber.h" int LDAPberval_from_object(PyObject *obj, struct berval *bv); int LDAPberval_from_object_check(PyObject *obj); void LDAPberval_release(struct berval *bv); PyObject *LDAPberval_to_object(const struct berval *bv); #endif /* __h_berval_ */ python-ldap-2.4.10/Modules/errors.h0000644000076400001440000000060711172071675017731 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: errors.h,v 1.6 2009/04/17 12:19:09 stroeder Exp $ */ #ifndef __h_errors_ #define __h_errors_ #include "common.h" #include "lber.h" #include "ldap.h" extern PyObject* LDAPexception_class; extern PyObject* LDAPerror( LDAP*, char*msg ); extern void LDAPinit_errors( PyObject* ); PyObject* LDAPerr(int errnum); #endif /* __h_errors */ python-ldap-2.4.10/Modules/version.h0000644000076400001440000000041611172071675020100 0ustar michaelusers00000000000000/* Set release version * See http://www.python-ldap.org/ for details. * $Id: version.h,v 1.4 2009/04/17 12:19:09 stroeder Exp $ */ #ifndef __h_version_ #define __h_version_ #include "common.h" extern void LDAPinit_version( PyObject* d ); #endif /* __h_version_ */ python-ldap-2.4.10/Modules/LDAPObject.c0000644000076400001440000011504211550563067020260 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: LDAPObject.c,v 1.90 2011/04/11 11:29:59 stroeder Exp $ */ #include "common.h" #include "patchlevel.h" #include #include #include "errors.h" #include "constants.h" #include "LDAPObject.h" #include "ldapcontrol.h" #include "message.h" #include "berval.h" #include "options.h" #ifdef HAVE_SASL #include #endif static void free_attrs(char***); /* constructor */ LDAPObject* newLDAPObject( LDAP* l ) { LDAPObject* self = (LDAPObject*) PyObject_NEW(LDAPObject, &LDAP_Type); if (self == NULL) return NULL; self->ldap = l; self->_save = NULL; self->valid = 1; return self; } /* destructor */ static void dealloc( LDAPObject* self ) { if (self->ldap) { if (self->valid) { LDAP_BEGIN_ALLOW_THREADS( self ); ldap_unbind_ext( self->ldap, NULL, NULL ); LDAP_END_ALLOW_THREADS( self ); self->valid = 0; } self->ldap = NULL; } PyObject_DEL(self); } /*------------------------------------------------------------ * utility functions */ /* * check to see if the LDAPObject is valid, * ie has been opened, and not closed. An exception is set if not valid. */ static int not_valid( LDAPObject* l ) { if (l->valid) { return 0; } else { PyErr_SetString( LDAPexception_class, "LDAP connection invalid" ); return 1; } } /* free a LDAPMod (complete or partially) allocated in Tuple_to_LDAPMod() */ static void LDAPMod_DEL( LDAPMod* lm ) { Py_ssize_t i; if (lm->mod_type) PyMem_DEL(lm->mod_type); if (lm->mod_bvalues) { for (i = 0; lm->mod_bvalues[i]; i++) { PyMem_DEL(lm->mod_bvalues[i]); } PyMem_DEL(lm->mod_bvalues); } PyMem_DEL(lm); } /* * convert a tuple of the form (int,str,[str,...]) * or (str, [str,...]) if no_op is true, into an LDAPMod structure. * See ldap_modify(3) for details. * * NOTE: the resulting LDAPMod structure has pointers directly into * the Python string storage, so LDAPMod structures MUST have a * shorter lifetime than the tuple passed in. */ /* XXX - there is no way to pass complex-structured BER objects in here! */ static LDAPMod* Tuple_to_LDAPMod( PyObject* tup, int no_op ) { int op; char *type; PyObject *list, *item; LDAPMod *lm = NULL; Py_ssize_t i, len, nstrs; if (!PyTuple_Check(tup)) { PyErr_SetObject(PyExc_TypeError, Py_BuildValue("sO", "expected a tuple", tup)); return NULL; } if (no_op) { if (!PyArg_ParseTuple( tup, "sO", &type, &list )) return NULL; op = 0; } else { if (!PyArg_ParseTuple( tup, "isO", &op, &type, &list )) return NULL; } lm = PyMem_NEW(LDAPMod, 1); if (lm == NULL) goto nomem; lm->mod_op = op | LDAP_MOD_BVALUES; lm->mod_bvalues = NULL; len = strlen(type); lm->mod_type = PyMem_NEW(char, len + 1); if (lm->mod_type == NULL) goto nomem; memcpy(lm->mod_type, type, len + 1); if (list == Py_None) { /* None indicates a NULL mod_bvals */ } else if (PyString_Check(list)) { /* Single string is a singleton list */ lm->mod_bvalues = PyMem_NEW(struct berval *, 2); if (lm->mod_bvalues == NULL) goto nomem; lm->mod_bvalues[0] = PyMem_NEW(struct berval, 1); if (lm->mod_bvalues[0] == NULL) goto nomem; lm->mod_bvalues[1] = NULL; lm->mod_bvalues[0]->bv_len = PyString_Size(list); lm->mod_bvalues[0]->bv_val = PyString_AsString(list); } else if (PySequence_Check(list)) { nstrs = PySequence_Length(list); lm->mod_bvalues = PyMem_NEW(struct berval *, nstrs + 1); if (lm->mod_bvalues == NULL) goto nomem; for (i = 0; i < nstrs; i++) { lm->mod_bvalues[i] = PyMem_NEW(struct berval, 1); if (lm->mod_bvalues[i] == NULL) goto nomem; lm->mod_bvalues[i+1] = NULL; item = PySequence_GetItem(list, i); if (item == NULL) goto error; if (!PyString_Check(item)) { PyErr_SetObject( PyExc_TypeError, Py_BuildValue( "sO", "expected a string in the list", item)); Py_DECREF(item); goto error; } lm->mod_bvalues[i]->bv_len = PyString_Size(item); lm->mod_bvalues[i]->bv_val = PyString_AsString(item); Py_DECREF(item); } if (nstrs == 0) lm->mod_bvalues[0] = NULL; } return lm; nomem: PyErr_NoMemory(); error: if (lm) LDAPMod_DEL(lm); return NULL; } /* free the structure allocated in List_to_LDAPMods() */ static void LDAPMods_DEL( LDAPMod** lms ) { LDAPMod** lmp; for ( lmp = lms; *lmp; lmp++ ) LDAPMod_DEL( *lmp ); PyMem_DEL(lms); } /* * convert a list of tuples into a LDAPMod*[] array structure * NOTE: list of tuples must live longer than the LDAPMods */ static LDAPMod** List_to_LDAPMods( PyObject *list, int no_op ) { Py_ssize_t i, len; LDAPMod** lms; PyObject *item; if (!PySequence_Check(list)) { PyErr_SetObject( PyExc_TypeError, Py_BuildValue("sO", "expected list of tuples", list )); return NULL; } len = PySequence_Length(list); if (len < 0) { PyErr_SetObject( PyExc_TypeError, Py_BuildValue("sO", "expected list of tuples", list )); return NULL; } lms = PyMem_NEW(LDAPMod *, len + 1); if (lms == NULL) goto nomem; for (i = 0; i < len; i++) { lms[i] = NULL; item = PySequence_GetItem(list, i); if (item == NULL) goto error; lms[i] = Tuple_to_LDAPMod(item, no_op); Py_DECREF(item); if (lms[i] == NULL) goto error; } lms[len] = NULL; return lms; nomem: PyErr_NoMemory(); error: if (lms) LDAPMods_DEL(lms); return NULL; } /* * convert a python list of strings into an attr list (char*[]). * returns 1 if successful, 0 if not (with exception set) * XXX the strings should live longer than the resulting attrs pointer. */ int attrs_from_List( PyObject *attrlist, char***attrsp ) { char **attrs = NULL; Py_ssize_t i, len; PyObject *item; if (attrlist == Py_None) { /* None means a NULL attrlist */ } else if (PyString_Check(attrlist)) { /* caught by John Benninghoff */ PyErr_SetObject( PyExc_TypeError, Py_BuildValue("sO", "expected *list* of strings, not a string", attrlist )); goto error; } else if (PySequence_Check(attrlist)) { len = PySequence_Length(attrlist); attrs = PyMem_NEW(char *, len + 1); if (attrs == NULL) goto nomem; for (i = 0; i < len; i++) { attrs[i] = NULL; item = PySequence_GetItem(attrlist, i); if (item == NULL) goto error; if (!PyString_Check(item)) { PyErr_SetObject(PyExc_TypeError, Py_BuildValue("sO", "expected string in list", item)); Py_DECREF(item); goto error; } attrs[i] = PyString_AsString(item); Py_DECREF(item); } attrs[len] = NULL; } else { PyErr_SetObject( PyExc_TypeError, Py_BuildValue("sO", "expected list of strings or None", attrlist )); goto error; } *attrsp = attrs; return 1; nomem: PyErr_NoMemory(); error: free_attrs(&attrs); return 0; } /* free memory allocated from above routine */ static void free_attrs( char*** attrsp ) { char **attrs = *attrsp; if (attrs != NULL) { PyMem_DEL(attrs); *attrsp = NULL; } } /*------------------------------------------------------------ * methods */ /* ldap_unbind_ext */ static PyObject* l_ldap_unbind_ext( LDAPObject* self, PyObject* args ) { PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int ldaperror; if (!PyArg_ParseTuple( args, "|OO", &serverctrls, &clientctrls)) return NULL; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_unbind_ext( self->ldap, server_ldcs, client_ldcs ); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_unbind_ext" ); self->valid = 0; Py_INCREF(Py_None); return Py_None; } /* ldap_abandon_ext */ static PyObject* l_ldap_abandon_ext( LDAPObject* self, PyObject* args ) { int msgid; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int ldaperror; if (!PyArg_ParseTuple( args, "i|OO", &msgid, &serverctrls, &clientctrls)) return NULL; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_abandon_ext( self->ldap, msgid, server_ldcs, client_ldcs ); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_abandon_ext" ); Py_INCREF(Py_None); return Py_None; } /* ldap_add_ext */ static PyObject * l_ldap_add_ext( LDAPObject* self, PyObject *args ) { char *dn; PyObject *modlist; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int msgid; int ldaperror; LDAPMod **mods; if (!PyArg_ParseTuple( args, "sO|OO", &dn, &modlist, &serverctrls, &clientctrls )) return NULL; if (not_valid(self)) return NULL; mods = List_to_LDAPMods( modlist, 1 ); if (mods == NULL) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_add_ext( self->ldap, dn, mods, server_ldcs, client_ldcs, &msgid); LDAP_END_ALLOW_THREADS( self ); LDAPMods_DEL( mods ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_add_ext" ); return PyInt_FromLong(msgid); } /* ldap_simple_bind */ static PyObject* l_ldap_simple_bind( LDAPObject* self, PyObject* args ) { char *who; int msgid; int ldaperror; Py_ssize_t cred_len; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; struct berval cred; if (!PyArg_ParseTuple( args, "ss#|OO", &who, &cred.bv_val, &cred_len, &serverctrls, &clientctrls )) return NULL; cred.bv_len = (ber_len_t) cred_len; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_sasl_bind( self->ldap, who, LDAP_SASL_SIMPLE, &cred, server_ldcs, client_ldcs, &msgid); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_simple_bind" ); return PyInt_FromLong( msgid ); } #ifdef HAVE_SASL /* The following functions implement SASL binds. A new method sasl_interactive_bind_s(bind_dn, sasl_mechanism) has been introduced. * The bind_dn argument will be passed to the c library; however, normally it is not needed and should be an empty string. * The sasl_mechanism argument is an instance of a class that implements a callback interface. For convenience, it should be derived from the sasl class (which lives in the ldap.sasl module). See the module documentation for more information. Check your /usr/lib/sasl/ directory for locally installed SASL auth modules ("mechanisms"), or try ldapsearch -b "" -s base -LLL -x supportedSASLMechanisms (perhaps with an additional -h and -p argument for ldap host and port). The latter will show you which SASL mechanisms are known to the LDAP server. If you do not want to set up Kerberos, you can still use SASL binds. Your authentication data should then be stored in /etc/sasldb (see saslpasswd(8)). If the LDAP server does not find the sasldb, it wont allow for DIGEST-MD5 and CRAM-MD5. One important thing to get started with sasldb: you should first add a dummy user (saslpasswd -c dummy), and this will give you some strange error messages. Then delete the dummy user (saslpasswd -d dummy), and now you can start adding users to your sasldb (again, use the -c switch). Strange, eh? * The sasl_mechanism object must implement a method, which will be called by the sasl lib several times. The prototype of the callback looks like this: callback(id, challenge, prompt, defresult) has to return a string (or maybe None). The id argument specifies, which information should be passed back to the SASL lib (see SASL_CB_xxx in sasl.h) A nice "Howto get LDAPv3 up and running with Kerberos and SSL" can be found at http://www.bayour.com/LDAPv3-HOWTO.html. Instead of MIT Kerberos, I used Heimdal for my tests (since it is included with SuSE Linux). Todo: * Find a better interface than the python callback. This is really ugly. Perhaps one could make use of a sasl class, like in the perl ldap module. * Thread safety? * Memory Management? * Write more docs * ... */ static int interaction ( unsigned flags, sasl_interact_t *interact, PyObject* SASLObject ) { /* const char *dflt = interact->defresult; */ PyObject *result; char *c_result; result = PyObject_CallMethod(SASLObject, "callback", "isss", interact->id, /* see sasl.h */ interact->challenge, interact->prompt, interact->defresult); if (result == NULL) /*searching for a better error code */ return LDAP_OPERATIONS_ERROR; c_result = PyString_AsString(result); /*xxx Error checking?? */ /* according to the sasl docs, we should malloc() the returned string only for calls where interact->id == SASL_CB_PASS, so we probably leak a few bytes per ldap bind. However, if I restrict the strdup() to this case, I get segfaults. Should probably be fixed sometimes. */ interact->result = strdup( c_result ); if (interact->result == NULL) return LDAP_OPERATIONS_ERROR; interact->len = strlen(c_result); /* We _should_ overwrite the python string buffer for security reasons, however we may not (api/stringObjects.html). Any ideas? */ Py_DECREF(result); /*not needed any longer */ result = NULL; return LDAP_SUCCESS; } /* This function will be called by ldap_sasl_interactive_bind(). The "*in" is an array of sasl_interact_t's (see sasl.h for a reference). The last interact in the array has an interact->id of SASL_CB_LIST_END. */ int py_ldap_sasl_interaction( LDAP *ld, unsigned flags, void *defaults, void *in ) { /* These are just typecasts */ sasl_interact_t *interact = (sasl_interact_t *) in; PyObject *SASLObject = (PyObject *) defaults; /* Loop over the array of sasl_interact_t structs */ while( interact->id != SASL_CB_LIST_END ) { int rc = 0; rc = interaction( flags, interact, SASLObject ); if( rc ) return rc; interact++; } return LDAP_SUCCESS; } static PyObject* l_ldap_sasl_interactive_bind_s( LDAPObject* self, PyObject* args ) { char *c_mechanism; char *who; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; PyObject *SASLObject = NULL; PyObject *mechanism = NULL; int msgid; static unsigned sasl_flags = LDAP_SASL_QUIET; /* * In Python 2.3+, a "I" format argument indicates that we're either converting * the Python object into a long or an unsigned int. In versions prior to that, * it will always convert to a long. Since the sasl_flags variable is an * unsigned int, we need to use the "I" flag if we're running Python 2.3+ and a * "i" otherwise. */ #if (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION < 3) if (!PyArg_ParseTuple(args, "sOOOi", &who, &SASLObject, &serverctrls, &clientctrls, &sasl_flags )) #else if (!PyArg_ParseTuple(args, "sOOOI", &who, &SASLObject, &serverctrls, &clientctrls, &sasl_flags )) #endif return NULL; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } /* now we extract the sasl mechanism from the SASL Object */ mechanism = PyObject_GetAttrString(SASLObject, "mech"); if (mechanism == NULL) return NULL; c_mechanism = PyString_AsString(mechanism); Py_DECREF(mechanism); mechanism = NULL; /* Don't know if it is the "intended use" of the defaults parameter of ldap_sasl_interactive_bind_s when we pass the Python object SASLObject, but passing it through some static variable would destroy thread safety, IMHO. */ msgid = ldap_sasl_interactive_bind_s(self->ldap, who, c_mechanism, (LDAPControl**) server_ldcs, (LDAPControl**) client_ldcs, sasl_flags, py_ldap_sasl_interaction, SASLObject); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if (msgid != LDAP_SUCCESS) return LDAPerror( self->ldap, "ldap_sasl_interactive_bind_s" ); return PyInt_FromLong( msgid ); } #endif #ifdef LDAP_API_FEATURE_CANCEL /* ldap_cancel */ static PyObject* l_ldap_cancel( LDAPObject* self, PyObject* args ) { int msgid; int cancelid; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int ldaperror; if (!PyArg_ParseTuple( args, "i|OO", &cancelid, &serverctrls, &clientctrls)) return NULL; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_cancel( self->ldap, cancelid, server_ldcs, client_ldcs, &msgid ); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_cancel" ); return PyInt_FromLong( msgid ); } #endif /* ldap_compare_ext */ static PyObject * l_ldap_compare_ext( LDAPObject* self, PyObject *args ) { char *dn, *attr; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int msgid; int ldaperror; Py_ssize_t value_len; struct berval value; if (!PyArg_ParseTuple( args, "sss#|OO", &dn, &attr, &value.bv_val, &value_len, &serverctrls, &clientctrls )) return NULL; value.bv_len = (ber_len_t) value_len; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_compare_ext( self->ldap, dn, attr, &value, server_ldcs, client_ldcs, &msgid ); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_compare_ext" ); return PyInt_FromLong( msgid ); } /* ldap_delete_ext */ static PyObject * l_ldap_delete_ext( LDAPObject* self, PyObject *args ) { char *dn; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int msgid; int ldaperror; if (!PyArg_ParseTuple( args, "s|OO", &dn, &serverctrls, &clientctrls )) return NULL; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_delete_ext( self->ldap, dn, server_ldcs, client_ldcs, &msgid ); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_delete_ext" ); return PyInt_FromLong(msgid); } /* ldap_modify_ext */ static PyObject * l_ldap_modify_ext( LDAPObject* self, PyObject *args ) { char *dn; PyObject *modlist; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int msgid; int ldaperror; LDAPMod **mods; if (!PyArg_ParseTuple( args, "sO|OO", &dn, &modlist, &serverctrls, &clientctrls )) return NULL; if (not_valid(self)) return NULL; mods = List_to_LDAPMods( modlist, 0 ); if (mods == NULL) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_modify_ext( self->ldap, dn, mods, server_ldcs, client_ldcs, &msgid ); LDAP_END_ALLOW_THREADS( self ); LDAPMods_DEL( mods ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_modify_ext" ); return PyInt_FromLong( msgid ); } /* ldap_rename */ static PyObject * l_ldap_rename( LDAPObject* self, PyObject *args ) { char *dn, *newrdn; char *newSuperior = NULL; int delold = 1; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int msgid; int ldaperror; if (!PyArg_ParseTuple( args, "ss|ziOO", &dn, &newrdn, &newSuperior, &delold, &serverctrls, &clientctrls )) return NULL; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_rename( self->ldap, dn, newrdn, newSuperior, delold, server_ldcs, client_ldcs, &msgid ); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_rename" ); return PyInt_FromLong( msgid ); } /* ldap_result4 */ static PyObject * l_ldap_result4( LDAPObject* self, PyObject *args ) { int msgid = LDAP_RES_ANY; int all = 1; double timeout = -1.0; int add_ctrls = 0; int add_intermediates = 0; int add_extop = 0; struct timeval tv; struct timeval* tvp; int res_type; LDAPMessage *msg = NULL; PyObject *result_str, *retval, *pmsg, *pyctrls = 0; int res_msgid = 0; char *retoid = 0; PyObject *valuestr = 0; if (!PyArg_ParseTuple( args, "|iidiii", &msgid, &all, &timeout, &add_ctrls, &add_intermediates, &add_extop )) return NULL; if (not_valid(self)) return NULL; if (timeout >= 0) { tvp = &tv; set_timeval_from_double( tvp, timeout ); } else { tvp = NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); res_type = ldap_result( self->ldap, msgid, all, tvp, &msg ); LDAP_END_ALLOW_THREADS( self ); if (res_type < 0) /* LDAP or system error */ return LDAPerror( self->ldap, "ldap_result4" ); if (res_type == 0) { /* Polls return (None, None, None, None); timeouts raise an exception */ if (timeout == 0) { if (add_extop) { return Py_BuildValue("(OOOOOO)", Py_None, Py_None, Py_None, Py_None, Py_None, Py_None); } else { return Py_BuildValue("(OOOO)", Py_None, Py_None, Py_None, Py_None); } } else return LDAPerr(LDAP_TIMEOUT); } if (msg) res_msgid = ldap_msgid(msg); int result = LDAP_SUCCESS; char **refs = NULL; LDAPControl **serverctrls = 0; LDAP_BEGIN_ALLOW_THREADS( self ); if (res_type == LDAP_RES_SEARCH_ENTRY) { /* LDAPmessage_to_python will parse entries and read the controls for each entry */ } else if (res_type == LDAP_RES_SEARCH_REFERENCE) { /* LDAPmessage_to_python will parse refs and read the controls for each res */ } else if (res_type == LDAP_RES_INTERMEDIATE) { /* LDAPmessage_to_python will parse intermediates and controls */ } else { int rc; if (res_type == LDAP_RES_EXTENDED) { struct berval *retdata = 0; rc = ldap_parse_extended_result( self->ldap, msg, &retoid, &retdata, 0 ); /* handle error rc!=0 here? */ if (rc == LDAP_SUCCESS) { valuestr = LDAPberval_to_object(retdata); } ber_bvfree( retdata ); } rc = ldap_parse_result( self->ldap, msg, &result, NULL, NULL, &refs, &serverctrls, 0 ); } LDAP_END_ALLOW_THREADS( self ); if (result != LDAP_SUCCESS) { /* result error */ char *e, err[1024]; if (result == LDAP_REFERRAL && refs && refs[0]) { snprintf(err, sizeof(err), "Referral:\n%s", refs[0]); e = err; } else e = "ldap_parse_result"; ldap_msgfree(msg); return LDAPerror( self->ldap, e ); } if (!(pyctrls = LDAPControls_to_List(serverctrls))) { int err = LDAP_NO_MEMORY; ldap_set_option(self->ldap, LDAP_OPT_ERROR_NUMBER, &err); ldap_msgfree(msg); return LDAPerror(self->ldap, "LDAPControls_to_List"); } ldap_controls_free(serverctrls); pmsg = LDAPmessage_to_python( self->ldap, msg, add_ctrls, add_intermediates ); result_str = LDAPconstant( res_type ); if (pmsg == NULL) { retval = NULL; } else { /* s handles NULL, but O does not */ if (add_extop) { retval = Py_BuildValue("(OOiOsO)", result_str, pmsg, res_msgid, pyctrls, retoid, valuestr ? valuestr : Py_None); } else { retval = Py_BuildValue("(OOiO)", result_str, pmsg, res_msgid, pyctrls); } if (pmsg != Py_None) { Py_DECREF(pmsg); } } if (valuestr) { Py_DECREF(valuestr); } Py_XDECREF(pyctrls); Py_DECREF(result_str); return retval; } /* ldap_search_ext */ static PyObject* l_ldap_search_ext( LDAPObject* self, PyObject* args ) { char *base; int scope; char *filter; PyObject *attrlist = Py_None; char **attrs; int attrsonly = 0; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; double timeout = -1.0; struct timeval tv; struct timeval* tvp; int sizelimit = 0; int msgid; int ldaperror; if (!PyArg_ParseTuple( args, "sis|OiOOdi", &base, &scope, &filter, &attrlist, &attrsonly, &serverctrls, &clientctrls, &timeout, &sizelimit )) return NULL; if (not_valid(self)) return NULL; if (!attrs_from_List( attrlist, &attrs )) return NULL; if (timeout >= 0) { tvp = &tv; set_timeval_from_double( tvp, timeout ); } else { tvp = NULL; } if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_search_ext( self->ldap, base, scope, filter, attrs, attrsonly, server_ldcs, client_ldcs, tvp, sizelimit, &msgid ); LDAP_END_ALLOW_THREADS( self ); free_attrs( &attrs ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_search_ext" ); return PyInt_FromLong( msgid ); } /* ldap_whoami_s (available since OpenLDAP 2.1.13) */ static PyObject* l_ldap_whoami_s( LDAPObject* self, PyObject* args ) { PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; struct berval *bvalue = NULL; PyObject *result; int ldaperror; if (!PyArg_ParseTuple( args, "|OO", &serverctrls, &clientctrls)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_whoami_s( self->ldap, &bvalue, server_ldcs, client_ldcs ); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_whoami_s" ); result = LDAPberval_to_object(bvalue); return result; } #ifdef HAVE_TLS /* ldap_start_tls_s */ static PyObject* l_ldap_start_tls_s( LDAPObject* self, PyObject* args ) { int result; if (!PyArg_ParseTuple( args, "" )) return NULL; if (not_valid(self)) return NULL; result = ldap_start_tls_s( self->ldap, NULL, NULL ); if ( result != LDAP_SUCCESS ){ ldap_set_option(self->ldap, LDAP_OPT_ERROR_NUMBER, &result); return LDAPerror( self->ldap, "ldap_start_tls_s" ); } Py_INCREF(Py_None); return Py_None; } #endif /* ldap_set_option */ static PyObject* l_ldap_set_option(PyObject* self, PyObject *args) { PyObject *value; int option; if (!PyArg_ParseTuple(args, "iO:set_option", &option, &value)) return NULL; if (LDAP_set_option((LDAPObject *)self, option, value) == -1) return NULL; Py_INCREF(Py_None); return Py_None; } /* ldap_get_option */ static PyObject* l_ldap_get_option(PyObject* self, PyObject *args) { int option; if (!PyArg_ParseTuple(args, "i:get_option", &option)) return NULL; return LDAP_get_option((LDAPObject *)self, option); } /* ldap_passwd */ static PyObject * l_ldap_passwd( LDAPObject* self, PyObject *args ) { struct berval user; Py_ssize_t user_len; struct berval oldpw; Py_ssize_t oldpw_len; struct berval newpw; Py_ssize_t newpw_len; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int msgid; int ldaperror; if (!PyArg_ParseTuple( args, "z#z#z#|OO", &user.bv_val, &user_len, &oldpw.bv_val, &oldpw_len, &newpw.bv_val, &newpw_len, &serverctrls, &clientctrls )) return NULL; user.bv_len = (ber_len_t) user_len; oldpw.bv_len = (ber_len_t) oldpw_len; newpw.bv_len = (ber_len_t) newpw_len; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_passwd( self->ldap, user.bv_val != NULL ? &user : NULL, oldpw.bv_val != NULL ? &oldpw : NULL, newpw.bv_val != NULL ? &newpw : NULL, server_ldcs, client_ldcs, &msgid ); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_passwd" ); return PyInt_FromLong( msgid ); } /* ldap_extended_operation */ static PyObject * l_ldap_extended_operation( LDAPObject* self, PyObject *args ) { char *reqoid = NULL; struct berval reqvalue = {0, NULL}; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; int msgid; int ldaperror; if (!PyArg_ParseTuple( args, "sz#|OO", &reqoid, &reqvalue.bv_val, &reqvalue.bv_len, &serverctrls, &clientctrls )) return NULL; if (not_valid(self)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) return NULL; } if (!PyNone_Check(clientctrls)) { if (!LDAPControls_from_object(clientctrls, &client_ldcs)) return NULL; } LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_extended_operation( self->ldap, reqoid, reqvalue.bv_val != NULL ? &reqvalue : NULL, server_ldcs, client_ldcs, &msgid ); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if ( ldaperror!=LDAP_SUCCESS ) return LDAPerror( self->ldap, "ldap_extended_operation" ); return PyInt_FromLong( msgid ); } /* methods */ static PyMethodDef methods[] = { {"unbind_ext", (PyCFunction)l_ldap_unbind_ext, METH_VARARGS }, {"abandon_ext", (PyCFunction)l_ldap_abandon_ext, METH_VARARGS }, {"add_ext", (PyCFunction)l_ldap_add_ext, METH_VARARGS }, {"simple_bind", (PyCFunction)l_ldap_simple_bind, METH_VARARGS }, #ifdef HAVE_SASL {"sasl_interactive_bind_s", (PyCFunction)l_ldap_sasl_interactive_bind_s, METH_VARARGS }, #endif {"compare_ext", (PyCFunction)l_ldap_compare_ext, METH_VARARGS }, {"delete_ext", (PyCFunction)l_ldap_delete_ext, METH_VARARGS }, {"modify_ext", (PyCFunction)l_ldap_modify_ext, METH_VARARGS }, {"rename", (PyCFunction)l_ldap_rename, METH_VARARGS }, {"result4", (PyCFunction)l_ldap_result4, METH_VARARGS }, {"search_ext", (PyCFunction)l_ldap_search_ext, METH_VARARGS }, #ifdef HAVE_TLS {"start_tls_s", (PyCFunction)l_ldap_start_tls_s, METH_VARARGS }, #endif {"whoami_s", (PyCFunction)l_ldap_whoami_s, METH_VARARGS }, {"passwd", (PyCFunction)l_ldap_passwd, METH_VARARGS }, {"set_option", (PyCFunction)l_ldap_set_option, METH_VARARGS }, {"get_option", (PyCFunction)l_ldap_get_option, METH_VARARGS }, #ifdef LDAP_API_FEATURE_CANCEL {"cancel", (PyCFunction)l_ldap_cancel, METH_VARARGS }, #endif {"extop", (PyCFunction)l_ldap_extended_operation, METH_VARARGS }, { NULL, NULL } }; /* get attribute */ static PyObject* getattr(LDAPObject* self, char* name) { return Py_FindMethod(methods, (PyObject*)self, name); } /* set attribute */ static int setattr(LDAPObject* self, char* name, PyObject* value) { PyErr_SetString(PyExc_AttributeError, name); return -1; } /* type entry */ PyTypeObject LDAP_Type = { #if defined(MS_WINDOWS) || defined(__CYGWIN__) /* see http://www.python.org/doc/FAQ.html#3.24 */ PyObject_HEAD_INIT(NULL) #else /* ! MS_WINDOWS */ PyObject_HEAD_INIT(&PyType_Type) #endif /* MS_WINDOWS */ 0, /*ob_size*/ "LDAP", /*tp_name*/ sizeof(LDAPObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)getattr, /*tp_getattr*/ (setattrfunc)setattr, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ }; python-ldap-2.4.10/Modules/options.c0000644000076400001440000002431211574511341020075 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: options.c,v 1.39 2011/06/10 21:55:45 stroeder Exp $ */ #include "common.h" #include "errors.h" #include "LDAPObject.h" #include "ldapcontrol.h" #include "options.h" void set_timeval_from_double( struct timeval *tv, double d ) { tv->tv_usec = (long) ( fmod(d, 1.0) * 1000000.0 ); tv->tv_sec = (long) floor(d); } /** * Converts a return code from ldap_set_option() or ldap_get_option() * into a python error, and returns NULL. */ static PyObject * option_error(int res, const char *fn) { if (res == LDAP_OPT_ERROR) PyErr_SetString(PyExc_ValueError, "option error"); else if (res == LDAP_PARAM_ERROR) PyErr_SetString(PyExc_ValueError, "parameter error"); else if (res == LDAP_NO_MEMORY) PyErr_NoMemory(); else PyErr_Format(PyExc_SystemError, "error %d from %s", res, fn); return NULL; } /** * Sets an LDAP option. * Returns 0 on failure, 1 on success */ int LDAP_set_option(LDAPObject *self, int option, PyObject *value) { int res; int intval; double doubleval; char *strval; struct timeval tv; void *ptr; LDAP *ld; LDAPControl **controls = NULL; ld = self ? self->ldap : NULL; switch(option) { case LDAP_OPT_API_INFO: case LDAP_OPT_API_FEATURE_INFO: #ifdef HAVE_SASL case LDAP_OPT_X_SASL_SSF: #endif /* Read-only options */ PyErr_SetString(PyExc_ValueError, "read-only option"); return 0; case LDAP_OPT_REFERRALS: case LDAP_OPT_RESTART: #ifdef LDAP_OPT_X_SASL_NOCANON case LDAP_OPT_X_SASL_NOCANON: #endif #ifdef LDAP_OPT_CONNECT_ASYNC case LDAP_OPT_CONNECT_ASYNC: #endif /* Truth-value options */ ptr = PyObject_IsTrue(value) ? LDAP_OPT_ON : LDAP_OPT_OFF; break; case LDAP_OPT_DEREF: case LDAP_OPT_SIZELIMIT: case LDAP_OPT_TIMELIMIT: case LDAP_OPT_PROTOCOL_VERSION: case LDAP_OPT_ERROR_NUMBER: case LDAP_OPT_DEBUG_LEVEL: #ifdef HAVE_TLS case LDAP_OPT_X_TLS: case LDAP_OPT_X_TLS_REQUIRE_CERT: #ifdef LDAP_OPT_X_TLS_CRLCHECK case LDAP_OPT_X_TLS_CRLCHECK: #endif #ifdef LDAP_OPT_X_TLS_NEWCTX case LDAP_OPT_X_TLS_NEWCTX: #endif #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN case LDAP_OPT_X_TLS_PROTOCOL_MIN: #endif #endif #ifdef HAVE_SASL case LDAP_OPT_X_SASL_SSF_MIN: case LDAP_OPT_X_SASL_SSF_MAX: #endif #ifdef LDAP_OPT_X_KEEPALIVE_IDLE case LDAP_OPT_X_KEEPALIVE_IDLE: #endif #ifdef LDAP_OPT_X_KEEPALIVE_PROBES case LDAP_OPT_X_KEEPALIVE_PROBES: #endif #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL case LDAP_OPT_X_KEEPALIVE_INTERVAL: #endif /* integer value options */ if (!PyArg_Parse(value, "i:set_option", &intval)) return 0; ptr = &intval; break; case LDAP_OPT_HOST_NAME: case LDAP_OPT_URI: #ifdef LDAP_OPT_DEFBASE case LDAP_OPT_DEFBASE: #endif case LDAP_OPT_ERROR_STRING: case LDAP_OPT_MATCHED_DN: #ifdef HAVE_TLS case LDAP_OPT_X_TLS_CACERTFILE: case LDAP_OPT_X_TLS_CACERTDIR: case LDAP_OPT_X_TLS_CERTFILE: case LDAP_OPT_X_TLS_KEYFILE: case LDAP_OPT_X_TLS_CIPHER_SUITE: case LDAP_OPT_X_TLS_RANDOM_FILE: case LDAP_OPT_X_TLS_DHFILE: #ifdef LDAP_OPT_X_TLS_CRLFILE case LDAP_OPT_X_TLS_CRLFILE: #endif #endif #ifdef HAVE_SASL case LDAP_OPT_X_SASL_SECPROPS: #endif /* String valued options */ if (!PyArg_Parse(value, "s:set_option", &strval)) return 0; ptr = strval; break; case LDAP_OPT_TIMEOUT: case LDAP_OPT_NETWORK_TIMEOUT: /* Float valued timeval options */ if (!PyArg_Parse(value, "d:set_option", &doubleval)) return 0; if (doubleval >= 0) { set_timeval_from_double( &tv, doubleval ); ptr = &tv; } else { ptr = NULL; } break; case LDAP_OPT_SERVER_CONTROLS: case LDAP_OPT_CLIENT_CONTROLS: if (!LDAPControls_from_object(value, &controls)) return 0; ptr = controls; break; default: PyErr_Format(PyExc_ValueError, "unknown option %d", option); return 0; } if (self) LDAP_BEGIN_ALLOW_THREADS(self); res = ldap_set_option(ld, option, ptr); if (self) LDAP_END_ALLOW_THREADS(self); if ((option == LDAP_OPT_SERVER_CONTROLS) || (option == LDAP_OPT_CLIENT_CONTROLS)) LDAPControl_List_DEL(controls); if (res != LDAP_OPT_SUCCESS) { option_error(res, "ldap_set_option"); return 0; } return 1; } PyObject * LDAP_get_option(LDAPObject *self, int option) { int res; int intval; struct timeval *tv; LDAPAPIInfo apiinfo; LDAPControl **lcs; LDAPControl *lc; char *strval; PyObject *extensions, *v, *tup; Py_ssize_t i, num_extensions, num_controls; LDAP *ld; ld = self ? self->ldap : NULL; switch(option) { case LDAP_OPT_API_INFO: apiinfo.ldapai_info_version = LDAP_API_INFO_VERSION; if (self) LDAP_BEGIN_ALLOW_THREADS(self); res = ldap_get_option( ld, option, &apiinfo ); if (self) LDAP_END_ALLOW_THREADS(self); if (res != LDAP_OPT_SUCCESS) return option_error(res, "ldap_get_option"); /* put the extensions into tuple form */ num_extensions = 0; while (apiinfo.ldapai_extensions[num_extensions]) num_extensions++; extensions = PyTuple_New(num_extensions); for (i = 0; i < num_extensions; i++) PyTuple_SET_ITEM(extensions, i, PyString_FromString(apiinfo.ldapai_extensions[i])); /* return api info as a dictionary */ v = Py_BuildValue("{s:i, s:i, s:i, s:s, s:i, s:O}", "info_version", apiinfo.ldapai_info_version, "api_version", apiinfo.ldapai_api_version, "protocol_version", apiinfo.ldapai_protocol_version, "vendor_name", apiinfo.ldapai_vendor_name, "vendor_version", apiinfo.ldapai_vendor_version, "extensions", extensions); if (apiinfo.ldapai_vendor_name) ldap_memfree(apiinfo.ldapai_vendor_name); for (i = 0; i < num_extensions; i++) ldap_memfree(apiinfo.ldapai_extensions[i]); ldap_memfree(apiinfo.ldapai_extensions); Py_DECREF(extensions); return v; #ifdef HAVE_SASL case LDAP_OPT_X_SASL_SSF: #endif case LDAP_OPT_REFERRALS: case LDAP_OPT_RESTART: case LDAP_OPT_DEREF: case LDAP_OPT_SIZELIMIT: case LDAP_OPT_TIMELIMIT: case LDAP_OPT_PROTOCOL_VERSION: case LDAP_OPT_ERROR_NUMBER: case LDAP_OPT_DEBUG_LEVEL: #ifdef HAVE_TLS case LDAP_OPT_X_TLS: case LDAP_OPT_X_TLS_REQUIRE_CERT: #ifdef LDAP_OPT_X_TLS_CRLCHECK case LDAP_OPT_X_TLS_CRLCHECK: #endif #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN case LDAP_OPT_X_TLS_PROTOCOL_MIN: #endif #endif #ifdef HAVE_SASL case LDAP_OPT_X_SASL_SSF_MIN: case LDAP_OPT_X_SASL_SSF_MAX: #endif #ifdef LDAP_OPT_X_SASL_NOCANON case LDAP_OPT_X_SASL_NOCANON: #endif #ifdef LDAP_OPT_CONNECT_ASYNC case LDAP_OPT_CONNECT_ASYNC: #endif #ifdef LDAP_OPT_X_KEEPALIVE_IDLE case LDAP_OPT_X_KEEPALIVE_IDLE: #endif #ifdef LDAP_OPT_X_KEEPALIVE_PROBES case LDAP_OPT_X_KEEPALIVE_PROBES: #endif #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL case LDAP_OPT_X_KEEPALIVE_INTERVAL: #endif /* Integer-valued options */ if (self) LDAP_BEGIN_ALLOW_THREADS(self); res = ldap_get_option(ld, option, &intval); if (self) LDAP_END_ALLOW_THREADS(self); if (res != LDAP_OPT_SUCCESS) return option_error(res, "ldap_get_option"); return PyInt_FromLong(intval); case LDAP_OPT_HOST_NAME: case LDAP_OPT_URI: #ifdef LDAP_OPT_DEFBASE case LDAP_OPT_DEFBASE: #endif case LDAP_OPT_ERROR_STRING: case LDAP_OPT_MATCHED_DN: #ifdef HAVE_TLS case LDAP_OPT_X_TLS_CACERTFILE: case LDAP_OPT_X_TLS_CACERTDIR: case LDAP_OPT_X_TLS_CERTFILE: case LDAP_OPT_X_TLS_KEYFILE: case LDAP_OPT_X_TLS_CIPHER_SUITE: case LDAP_OPT_X_TLS_RANDOM_FILE: case LDAP_OPT_X_TLS_DHFILE: #ifdef LDAP_OPT_X_TLS_CRLFILE case LDAP_OPT_X_TLS_CRLFILE: #endif #ifdef LDAP_OPT_X_TLS_PACKAGE case LDAP_OPT_X_TLS_PACKAGE: #endif #endif #ifdef HAVE_SASL case LDAP_OPT_X_SASL_SECPROPS: case LDAP_OPT_X_SASL_MECH: case LDAP_OPT_X_SASL_REALM: case LDAP_OPT_X_SASL_AUTHCID: case LDAP_OPT_X_SASL_AUTHZID: #ifdef LDAP_OPT_X_SASL_USERNAME case LDAP_OPT_X_SASL_USERNAME: #endif #endif /* String-valued options */ if (self) LDAP_BEGIN_ALLOW_THREADS(self); res = ldap_get_option(ld, option, &strval); if (self) LDAP_END_ALLOW_THREADS(self); if (res != LDAP_OPT_SUCCESS) return option_error(res, "ldap_get_option"); if (strval == NULL) { Py_INCREF(Py_None); return Py_None; } v = PyString_FromString(strval); ldap_memfree(strval); return v; case LDAP_OPT_TIMEOUT: case LDAP_OPT_NETWORK_TIMEOUT: /* Double-valued timeval options */ if (self) LDAP_BEGIN_ALLOW_THREADS(self); res = ldap_get_option(ld, option, &tv); if (self) LDAP_END_ALLOW_THREADS(self); if (res != LDAP_OPT_SUCCESS) return option_error(res, "ldap_get_option"); if (tv == NULL) { Py_INCREF(Py_None); return Py_None; } v = PyFloat_FromDouble( (double) tv->tv_sec + ( (double) tv->tv_usec / 1000000.0 ) ); ldap_memfree(tv); return v; case LDAP_OPT_SERVER_CONTROLS: case LDAP_OPT_CLIENT_CONTROLS: if (self) LDAP_BEGIN_ALLOW_THREADS(self); res = ldap_get_option(ld, option, &lcs); if (self) LDAP_END_ALLOW_THREADS(self); if (res != LDAP_OPT_SUCCESS) return option_error(res, "ldap_get_option"); if (lcs == NULL) return PyList_New(0); /* Get the number of controls */ num_controls = 0; while (lcs[num_controls]) num_controls++; /* We'll build a list of controls, with each control a tuple */ v = PyList_New(num_controls); for (i = 0; i < num_controls; i++) { lc = lcs[i]; tup = Py_BuildValue("(sbs)", lc->ldctl_oid, lc->ldctl_iscritical, lc->ldctl_value.bv_val); PyList_SET_ITEM(v, i, tup); } ldap_controls_free(lcs); return v; default: PyErr_Format(PyExc_ValueError, "unknown option %d", option); return NULL; } } python-ldap-2.4.10/Modules/errors.c0000644000076400001440000001326311704305353017720 0ustar michaelusers00000000000000/* * errors that arise from ldap use * Most errors become their own exception * See http://www.python-ldap.org/ for details. * $Id: errors.c,v 1.23 2012/01/11 10:04:48 stroeder Exp $ */ #include "common.h" #include "errors.h" /* the base exception class */ PyObject* LDAPexception_class; /* list of error objects */ #define LDAP_ERROR_MIN LDAP_REFERRAL_LIMIT_EXCEEDED #ifdef LDAP_PROXIED_AUTHORIZATION_DENIED #define LDAP_ERROR_MAX LDAP_PROXIED_AUTHORIZATION_DENIED #else #ifdef LDAP_ASSERTION_FAILED #define LDAP_ERROR_MAX LDAP_ASSERTION_FAILED #else #define LDAP_ERROR_MAX LDAP_OTHER #endif #endif #define LDAP_ERROR_OFFSET -LDAP_ERROR_MIN static PyObject* errobjects[ LDAP_ERROR_MAX-LDAP_ERROR_MIN+1 ]; /* Convert a bare LDAP error number into an exception */ PyObject* LDAPerr(int errnum) { if (errnum >= LDAP_ERROR_MIN && errnum <= LDAP_ERROR_MAX) PyErr_SetNone(errobjects[errnum+LDAP_ERROR_OFFSET]); else PyErr_SetObject(LDAPexception_class, Py_BuildValue("{s:i}", "errnum", errnum)); return NULL; } /* Convert an LDAP error into an informative python exception */ PyObject* LDAPerror( LDAP *l, char *msg ) { if (l == NULL) { PyErr_SetFromErrno( LDAPexception_class ); return NULL; } else { int errnum, opt_errnum; PyObject *errobj; PyObject *info; PyObject *str; char *matched, *error; opt_errnum = ldap_get_option(l, LDAP_OPT_ERROR_NUMBER, &errnum); if (opt_errnum != LDAP_OPT_SUCCESS) errnum = opt_errnum; if (errnum == LDAP_NO_MEMORY) return PyErr_NoMemory(); if (errnum >= LDAP_ERROR_MIN && errnum <= LDAP_ERROR_MAX) errobj = errobjects[errnum+LDAP_ERROR_OFFSET]; else errobj = LDAPexception_class; info = PyDict_New(); if (info == NULL) return NULL; str = PyString_FromString(ldap_err2string(errnum)); if (str) PyDict_SetItemString( info, "desc", str ); Py_XDECREF(str); if (ldap_get_option(l, LDAP_OPT_MATCHED_DN, &matched) >= 0 && matched != NULL) { if (*matched != '\0') { str = PyString_FromString(matched); if (str) PyDict_SetItemString( info, "matched", str ); Py_XDECREF(str); } ldap_memfree(matched); } if (errnum == LDAP_REFERRAL) { str = PyString_FromString(msg); if (str) PyDict_SetItemString( info, "info", str ); Py_XDECREF(str); } else if (ldap_get_option(l, LDAP_OPT_ERROR_STRING, &error) >= 0 && error != NULL) { if (error != '\0') { str = PyString_FromString(error); if (str) PyDict_SetItemString( info, "info", str ); Py_XDECREF(str); } ldap_memfree(error); } PyErr_SetObject( errobj, info ); Py_DECREF(info); return NULL; } } /* initialisation */ void LDAPinit_errors( PyObject*d ) { /* create the base exception class */ LDAPexception_class = PyErr_NewException("ldap.LDAPError", NULL, NULL); PyDict_SetItemString( d, "LDAPError", LDAPexception_class ); /* XXX - backward compatibility with pre-1.8 */ PyDict_SetItemString( d, "error", LDAPexception_class ); /* create each LDAP error object */ # define seterrobj2(n,o) \ PyDict_SetItemString( d, #n, (errobjects[LDAP_##n+LDAP_ERROR_OFFSET] = o) ) # define seterrobj(n) { \ PyObject *e = PyErr_NewException("ldap." #n, \ LDAPexception_class, NULL); \ seterrobj2(n, e); \ Py_INCREF(e); \ } seterrobj(ADMINLIMIT_EXCEEDED); seterrobj(AFFECTS_MULTIPLE_DSAS); seterrobj(ALIAS_DEREF_PROBLEM); seterrobj(ALIAS_PROBLEM); seterrobj(ALREADY_EXISTS); seterrobj(AUTH_UNKNOWN); seterrobj(BUSY); seterrobj(CLIENT_LOOP); seterrobj(COMPARE_FALSE); seterrobj(COMPARE_TRUE); seterrobj(CONFIDENTIALITY_REQUIRED); seterrobj(CONNECT_ERROR); seterrobj(CONSTRAINT_VIOLATION); seterrobj(CONTROL_NOT_FOUND); seterrobj(DECODING_ERROR); seterrobj(ENCODING_ERROR); seterrobj(FILTER_ERROR); seterrobj(INAPPROPRIATE_AUTH); seterrobj(INAPPROPRIATE_MATCHING); seterrobj(INSUFFICIENT_ACCESS); seterrobj(INVALID_CREDENTIALS); seterrobj(INVALID_DN_SYNTAX); seterrobj(INVALID_SYNTAX); seterrobj(IS_LEAF); seterrobj(LOCAL_ERROR); seterrobj(LOOP_DETECT); seterrobj(MORE_RESULTS_TO_RETURN); seterrobj(NAMING_VIOLATION); seterrobj(NO_OBJECT_CLASS_MODS); seterrobj(NOT_ALLOWED_ON_NONLEAF); seterrobj(NOT_ALLOWED_ON_RDN); seterrobj(NOT_SUPPORTED); seterrobj(NO_MEMORY); seterrobj(NO_OBJECT_CLASS_MODS); seterrobj(NO_RESULTS_RETURNED); seterrobj(NO_SUCH_ATTRIBUTE); seterrobj(NO_SUCH_OBJECT); seterrobj(OBJECT_CLASS_VIOLATION); seterrobj(OPERATIONS_ERROR); seterrobj(OTHER); seterrobj(PARAM_ERROR); seterrobj(PARTIAL_RESULTS); seterrobj(PROTOCOL_ERROR); seterrobj(REFERRAL); seterrobj(REFERRAL_LIMIT_EXCEEDED); seterrobj(RESULTS_TOO_LARGE); seterrobj(SASL_BIND_IN_PROGRESS); seterrobj(SERVER_DOWN); seterrobj(SIZELIMIT_EXCEEDED); seterrobj(STRONG_AUTH_NOT_SUPPORTED); seterrobj(STRONG_AUTH_REQUIRED); seterrobj(SUCCESS); seterrobj(TIMELIMIT_EXCEEDED); seterrobj(TIMEOUT); seterrobj(TYPE_OR_VALUE_EXISTS); seterrobj(UNAVAILABLE); seterrobj(UNAVAILABLE_CRITICAL_EXTENSION); seterrobj(UNDEFINED_TYPE); seterrobj(UNWILLING_TO_PERFORM); seterrobj(USER_CANCELLED); #ifdef LDAP_API_FEATURE_CANCEL seterrobj(CANCELLED); seterrobj(NO_SUCH_OPERATION); seterrobj(TOO_LATE); seterrobj(CANNOT_CANCEL); #endif #ifdef LDAP_ASSERTION_FAILED seterrobj(ASSERTION_FAILED); #endif #ifdef LDAP_PROXIED_AUTHORIZATION_DENIED seterrobj(PROXIED_AUTHORIZATION_DENIED); #endif } python-ldap-2.4.10/Modules/options.h0000644000076400001440000000053111172071675020104 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: options.h,v 1.4 2009/04/17 12:19:09 stroeder Exp $ */ int LDAP_optionval_by_name(const char *name); int LDAP_set_option(LDAPObject *self, int option, PyObject *value); PyObject *LDAP_get_option(LDAPObject *self, int option); void set_timeval_from_double( struct timeval *tv, double d ); python-ldap-2.4.10/Modules/message.c0000644000076400001440000001536511660514462020041 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: message.c,v 1.19 2011/10/26 18:38:06 stroeder Exp $ */ #include "common.h" #include "message.h" #include "berval.h" #include "errors.h" #include "ldapcontrol.h" #include "constants.h" /* * Converts an LDAP message into a Python structure. * * On success, returns a list of dictionaries. * On failure, returns NULL, and sets an error. * * The message m is always freed, regardless of return value. * * If add_ctrls is non-zero, per-entry/referral/partial/intermediate * controls will be added as a third item to each entry tuple * * If add_intermediates is non-zero, intermediate/partial results will * be returned */ PyObject * LDAPmessage_to_python(LDAP *ld, LDAPMessage *m, int add_ctrls, int add_intermediates) { /* we convert an LDAP message into a python structure. * It is always a list of dictionaries. * We always free m. */ PyObject *result, *pyctrls = 0; LDAPMessage* entry; LDAPControl **serverctrls = 0; int rc; result = PyList_New(0); if (result == NULL) { ldap_msgfree( m ); return NULL; } for(entry = ldap_first_entry(ld,m); entry != NULL; entry = ldap_next_entry(ld,entry)) { char *dn; char *attr; BerElement *ber = NULL; PyObject* entrytuple; PyObject* attrdict; dn = ldap_get_dn( ld, entry ); if (dn == NULL) { Py_DECREF(result); ldap_msgfree( m ); return LDAPerror( ld, "ldap_get_dn" ); } attrdict = PyDict_New(); if (attrdict == NULL) { Py_DECREF(result); ldap_msgfree( m ); ldap_memfree(dn); return NULL; } rc = ldap_get_entry_controls( ld, entry, &serverctrls ); if (rc) { Py_DECREF(result); ldap_msgfree( m ); ldap_memfree(dn); return LDAPerror( ld, "ldap_get_entry_controls" ); } /* convert serverctrls to list of tuples */ if ( ! ( pyctrls = LDAPControls_to_List( serverctrls ) ) ) { int err = LDAP_NO_MEMORY; ldap_set_option( ld, LDAP_OPT_ERROR_NUMBER, &err ); Py_DECREF(result); ldap_msgfree( m ); ldap_memfree(dn); ldap_controls_free(serverctrls); return LDAPerror( ld, "LDAPControls_to_List" ); } ldap_controls_free(serverctrls); /* Fill attrdict with lists */ for( attr = ldap_first_attribute( ld, entry, &ber ); attr != NULL; attr = ldap_next_attribute( ld, entry, ber ) ) { PyObject* valuelist; struct berval ** bvals = ldap_get_values_len( ld, entry, attr ); /* Find which list to append to */ if ( PyMapping_HasKeyString( attrdict, attr ) ) { valuelist = PyMapping_GetItemString( attrdict, attr ); } else { valuelist = PyList_New(0); if (valuelist != NULL && PyMapping_SetItemString(attrdict, attr, valuelist) == -1) { Py_DECREF(valuelist); valuelist = NULL; /* catch error later */ } } if (valuelist == NULL) { Py_DECREF(attrdict); Py_DECREF(result); if (ber != NULL) ber_free(ber, 0); ldap_msgfree( m ); ldap_memfree(attr); ldap_memfree(dn); Py_XDECREF(pyctrls); return NULL; } if (bvals != NULL) { Py_ssize_t i; for (i=0; bvals[i]; i++) { PyObject *valuestr; valuestr = LDAPberval_to_object(bvals[i]); if (PyList_Append( valuelist, valuestr ) == -1) { Py_DECREF(attrdict); Py_DECREF(result); Py_DECREF(valuestr); Py_DECREF(valuelist); if (ber != NULL) ber_free(ber, 0); ldap_msgfree( m ); ldap_memfree(attr); ldap_memfree(dn); Py_XDECREF(pyctrls); return NULL; } Py_DECREF(valuestr); } ldap_value_free_len(bvals); } Py_DECREF( valuelist ); ldap_memfree(attr); } if (add_ctrls) { entrytuple = Py_BuildValue("(sOO)", dn, attrdict, pyctrls); } else { entrytuple = Py_BuildValue("(sO)", dn, attrdict); } ldap_memfree(dn); Py_DECREF(attrdict); Py_XDECREF(pyctrls); PyList_Append(result, entrytuple); Py_DECREF(entrytuple); if (ber != NULL) ber_free(ber, 0); } for(entry = ldap_first_reference(ld,m); entry != NULL; entry = ldap_next_reference(ld,entry)) { char **refs = NULL; PyObject* entrytuple; PyObject* reflist = PyList_New(0); if (reflist == NULL) { Py_DECREF(result); ldap_msgfree( m ); return NULL; } if (ldap_parse_reference(ld, entry, &refs, &serverctrls, 0) != LDAP_SUCCESS) { Py_DECREF(reflist); Py_DECREF(result); ldap_msgfree( m ); return LDAPerror( ld, "ldap_parse_reference" ); } /* convert serverctrls to list of tuples */ if ( ! ( pyctrls = LDAPControls_to_List( serverctrls ) ) ) { int err = LDAP_NO_MEMORY; ldap_set_option( ld, LDAP_OPT_ERROR_NUMBER, &err ); Py_DECREF(reflist); Py_DECREF(result); ldap_msgfree( m ); ldap_controls_free(serverctrls); return LDAPerror( ld, "LDAPControls_to_List" ); } ldap_controls_free(serverctrls); if (refs) { Py_ssize_t i; for (i=0; refs[i] != NULL; i++) { PyObject *refstr = PyString_FromString(refs[i]); PyList_Append(reflist, refstr); Py_DECREF(refstr); } ber_memvfree( (void **) refs ); } if (add_ctrls) { entrytuple = Py_BuildValue("(sOO)", NULL, reflist, pyctrls); } else { entrytuple = Py_BuildValue("(sO)", NULL, reflist); } Py_DECREF(reflist); Py_XDECREF(pyctrls); PyList_Append(result, entrytuple); Py_DECREF(entrytuple); } if (add_intermediates) { for(entry = ldap_first_message(ld,m); entry != NULL; entry = ldap_next_message(ld,entry)) { /* list of tuples */ /* each tuple is OID, Berval, controllist */ if ( LDAP_RES_INTERMEDIATE == ldap_msgtype( entry ) ) { PyObject* valtuple; PyObject *valuestr; char *retoid = 0; struct berval *retdata = 0; if (ldap_parse_intermediate( ld, entry, &retoid, &retdata, &serverctrls, 0 ) != LDAP_SUCCESS) { Py_DECREF(result); ldap_msgfree( m ); return LDAPerror( ld, "ldap_parse_intermediate" ); } /* convert serverctrls to list of tuples */ if ( ! ( pyctrls = LDAPControls_to_List( serverctrls ) ) ) { int err = LDAP_NO_MEMORY; ldap_set_option( ld, LDAP_OPT_ERROR_NUMBER, &err ); Py_DECREF(result); ldap_msgfree( m ); ldap_controls_free(serverctrls); ldap_memfree( retoid ); ber_bvfree( retdata ); return LDAPerror( ld, "LDAPControls_to_List" ); } ldap_controls_free(serverctrls); valuestr = LDAPberval_to_object(retdata); ber_bvfree( retdata ); valtuple = Py_BuildValue("(sOO)", retoid, valuestr ? valuestr : Py_None, pyctrls); ldap_memfree( retoid ); Py_DECREF(valuestr); Py_XDECREF(pyctrls); PyList_Append(result, valtuple); Py_DECREF(valtuple); } } } ldap_msgfree( m ); return result; } python-ldap-2.4.10/Modules/constants.h0000644000076400001440000000111011172071675020417 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: constants.h,v 1.6 2009/04/17 12:19:09 stroeder Exp $ */ #ifndef __h_constants_ #define __h_constants_ #include "common.h" extern void LDAPinit_constants( PyObject* d ); extern PyObject* LDAPconstant( int ); #ifndef LDAP_CONTROL_PAGE_OID #define LDAP_CONTROL_PAGE_OID "1.2.840.113556.1.4.319" #endif /* !LDAP_CONTROL_PAGE_OID */ #ifndef LDAP_CONTROL_VALUESRETURNFILTER #define LDAP_CONTROL_VALUESRETURNFILTER "1.2.826.0.1.3344810.2.3" /* RFC 3876 */ #endif /* !LDAP_CONTROL_VALUESRETURNFILTER */ #endif /* __h_constants_ */ python-ldap-2.4.10/Modules/functions.c0000644000076400001440000000623111242162011020376 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: functions.c,v 1.27 2009/08/17 05:00:57 leonard Exp $ */ #include "common.h" #include "functions.h" #include "LDAPObject.h" #include "berval.h" #include "errors.h" #include "options.h" /* ldap_initialize */ static PyObject* l_ldap_initialize(PyObject* unused, PyObject *args) { char *uri; LDAP *ld = NULL; int ret; if (!PyArg_ParseTuple(args, "s", &uri)) return NULL; Py_BEGIN_ALLOW_THREADS ret = ldap_initialize(&ld, uri); Py_END_ALLOW_THREADS if (ret != LDAP_SUCCESS) return LDAPerror(ld, "ldap_initialize"); return (PyObject*)newLDAPObject(ld); } /* ldap_str2dn */ static PyObject* l_ldap_str2dn( PyObject* unused, PyObject *args ) { struct berval str; LDAPDN dn; int flags = 0; PyObject *result = NULL, *tmp; int res, i, j; Py_ssize_t str_len; /* * From a DN string such as "a=b,c=d;e=f", build * a list-equivalent of AVA structures; namely: * ((('a','b',1),('c','d',1)),(('e','f',1),)) * The integers are a bit combination of the AVA_* flags */ if (!PyArg_ParseTuple( args, "z#|i:str2dn", &str.bv_val, &str_len, &flags )) return NULL; str.bv_len = (ber_len_t) str_len; res = ldap_bv2dn(&str, &dn, flags); if (res != LDAP_SUCCESS) return LDAPerr(res); tmp = PyList_New(0); if (!tmp) goto failed; for (i = 0; dn[i]; i++) { LDAPRDN rdn; PyObject *rdnlist; rdn = dn[i]; rdnlist = PyList_New(0); if (!rdnlist) goto failed; if (PyList_Append(tmp, rdnlist) == -1) { Py_DECREF(rdnlist); goto failed; } for (j = 0; rdn[j]; j++) { LDAPAVA *ava = rdn[j]; PyObject *tuple; tuple = Py_BuildValue("(O&O&i)", LDAPberval_to_object, &ava->la_attr, LDAPberval_to_object, &ava->la_value, ava->la_flags & ~(LDAP_AVA_FREE_ATTR|LDAP_AVA_FREE_VALUE)); if (!tuple) { Py_DECREF(rdnlist); goto failed; } if (PyList_Append(rdnlist, tuple) == -1) { Py_DECREF(tuple); goto failed; } Py_DECREF(tuple); } Py_DECREF(rdnlist); } result = tmp; tmp = NULL; failed: Py_XDECREF(tmp); ldap_dnfree(dn); return result; } /* ldap_set_option (global options) */ static PyObject* l_ldap_set_option(PyObject* self, PyObject *args) { PyObject *value; int option; if (!PyArg_ParseTuple(args, "iO:set_option", &option, &value)) return NULL; if (!LDAP_set_option(NULL, option, value)) return NULL; Py_INCREF(Py_None); return Py_None; } /* ldap_get_option (global options) */ static PyObject* l_ldap_get_option(PyObject* self, PyObject *args) { int option; if (!PyArg_ParseTuple(args, "i:get_option", &option)) return NULL; return LDAP_get_option(NULL, option); } /* methods */ static PyMethodDef methods[] = { { "initialize", (PyCFunction)l_ldap_initialize, METH_VARARGS }, { "str2dn", (PyCFunction)l_ldap_str2dn, METH_VARARGS }, { "set_option", (PyCFunction)l_ldap_set_option, METH_VARARGS }, { "get_option", (PyCFunction)l_ldap_get_option, METH_VARARGS }, { NULL, NULL } }; /* initialisation */ void LDAPinit_functions( PyObject* d ) { LDAPadd_methods( d, methods ); } python-ldap-2.4.10/Modules/berval.c0000644000076400001440000000466711242133473017666 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: berval.c,v 1.1 2009/08/17 01:49:47 leonard Exp $ */ #include "common.h" #include "berval.h" /* * Converts a Python object into a data for a berval structure. * * New memory is allocated, and the content of the object is copied into it. * Then the (pre-existing) berval structure's field are filled in with pointer * and length data. * * The source object must implement the buffer interface, or be None. * If the source object is None, bv->bv_val will be set to NULL and bv_len to 0. * Otherwise, bv->bv_val will be non-NULL (even for zero-length data). * This allows the caller to distinguish a None argument as something special. * * Returns 0 on failure, leaving *bv unchanged, and setting an error. * Returns 1 on success: the berval must be freed with LDAPberval_release(). */ int LDAPberval_from_object(PyObject *obj, struct berval *bv) { const void *data; char *datacp; Py_ssize_t len; if (PyNone_Check(obj)) { bv->bv_len = 0; bv->bv_val = NULL; return 1; } if (!PyObject_AsReadBuffer(obj, &data, &len)) return 0; datacp = PyMem_MALLOC(len ? len : 1); if (!datacp) { PyErr_NoMemory(); return 0; } memcpy(datacp, data, len); bv->bv_len = len; bv->bv_val = datacp; return 1; } /* * Returns true if the object could be used to initialize a berval structure * with LDAPberval_from_object() */ int LDAPberval_from_object_check(PyObject *obj) { return PyNone_Check(obj) || PyObject_CheckReadBuffer(obj); } /* * Releases memory allocated by LDAPberval_from_object(). * Has no effect if the berval pointer is NULL or the berval data is NULL. */ void LDAPberval_release(struct berval *bv) { if (bv && bv->bv_val) { PyMem_FREE(bv->bv_val); bv->bv_len = 0; bv->bv_val = NULL; } } /* * Copies out the data from a berval, and returns it as a new Python object, * Returns None if the berval pointer is NULL. * * Note that this function is not the exact inverse of LDAPberval_from_object * with regards to the NULL/None conversion. * * Returns a new Python object on success, or NULL on failure. */ PyObject * LDAPberval_to_object(const struct berval *bv) { PyObject *ret = NULL; if (!bv) { ret = Py_None; Py_INCREF(ret); } else { ret = PyString_FromStringAndSize(bv->bv_val, bv->bv_len); } return ret; } python-ldap-2.4.10/.cvsignore0000644000076400001440000000005107507671454016635 0ustar michaelusers00000000000000build dist MANIFEST *.pyc *.pyo *.bck *~ python-ldap-2.4.10/TODO0000644000076400001440000000214611764172556015333 0ustar michaelusers00000000000000*** List of things to-do in no particular order *** General: - Define common Python style license for all modules - Support Python 3.2+ Modules/ - Specific support for more controls - VLV control - server-side sorting control - Wrap libldif as faster drop-in replacement for module ldif? - Attach response controls to LDAPError instances to deliver the controls to the calling application in case of an error - Attach more useful information to LDAPError instances, e.g. the filter used when doing a search in case of ldap.FILTER_ERROR etc. Lib/ - Unicode support for DNs, filter strings, etc. but not entry attributes! (Everybody asking for the latter should check the mailing list archive first.) - Caching of search requests for each LDAPObject instance - LDIF parser for replication logs and change records - DSMLv2 support Tests/ - Clean up and finish the mess of small test scripts started. - Create a test script that exercises everything with a server holding the BLITS test data set ------------------------------------------------------------------ $Id: TODO,v 1.38 2012/01/25 19:24:40 stroeder Exp $ python-ldap-2.4.10/INSTALL0000644000076400001440000000140011545721637015661 0ustar michaelusers00000000000000------------------------------ Installing python-ldap ------------------------------ Prerequisites: Required: - Python 2.3 or newer (see http://www.python.org) - OpenLDAP 2.4.11+ client libs (see http://www.openldap.org) It is not possible and not supported by the python-ldap project to build with prior versions. Optional dependencies of OpenLDAP libs: - Cyrus SASL 2.1.x or newer (see http://asg.web.cmu.edu/sasl/sasl-library.html) - OpenSSL 0.9.7 or newer (see http://www.openssl.org) - MIT Kerberos or heimdal libs Quick build instructions: edit setup.cfg (see Build/ for platform-specific examples) python setup.py build python setup.py install $Id: INSTALL,v 1.10 2011/04/02 22:30:55 stroeder Exp $ python-ldap-2.4.10/CHANGES0000644000076400001440000007743111764172556015647 0ustar michaelusers00000000000000---------------------------------------------------------------- Released 2.4.10 2012-06-07 Changes since 2.4.9: Lib/ * ldapobject.ReconnectLDAPObject.reconnect() now preserves order of options set with LDAPObject.set_option before. This is needed e.g. for setting connection-specific TLS options. Demo/ * Better version of Demo/pyasn1/syncrepl.py (thanks to Ben Cooksley) ---------------------------------------------------------------- Released 2.4.9 2012-03-14 Changes since 2.4.8: Lib/ * ldapobject.ReconnectLDAPObject.reconnect() now does kind of an internal locking to pause other threads while reconnecting is pending. * Changes to bind- and startTLS-related operation methods of class ReconnectLDAPObject for more robustness * New constant ldap.OPT_NAMES_DICT contains mapping from integer to variable name for all option-related constants. ---------------------------------------------------------------- Released 2.4.8 2012-02-21 Changes since 2.4.7: Lib/ * Fixed overzealous check for non-unique NAMEs in ldap.schema.subentry.SubSchema.__init__() * Fixed typos in control decoding method ldap.controls.simple.OctetStringInteger.decodeControlValue() * Added experimental support for draft-vchu-ldap-pwd-policy ---------------------------------------------------------------- Released 2.4.7 2012-12-19 Changes since 2.4.6: Lib/ * Separate classes for request/response controls for RFC 3829 * Fixed ldap.schema.subentry.SubSchema.attribute_types() to also eliminate double attribute types in MAY clause of DIT content rule Modules/ * Fixed memory leak (thanks to David Malcolm) ---------------------------------------------------------------- Released 2.4.6 2011-11-27 Changes since 2.4.5: Lib/ * ldap.controls.ppolicy: Another fix for decoding the password policy response control ---------------------------------------------------------------- Released 2.4.5 2011-11-25 Changes since 2.4.4: Installation: * defines for SASL and SSL in setup.cfg to be more friendly to Python setup tools (easy_install) Lib/ * Fixed typo in ldap.functions._ldap_function_call() which always released ldap._ldap_module_lock instead of local lock * ldap.controls.ppolicy: Fixed decoding the password policy response control Demo/ * Demo script for ldap.controls.ppolicy ---------------------------------------------------------------- Released 2.4.4 2011-10-26 Changes since 2.4.3: Modules/ * Format intermediate messages as 3-tuples instead of 4-tuples to match the format of other response messages. (thanks to Chris Mikkelson) * Fixes for memory leaks (thanks to Chris Mikkelson) Lib/ * New experimental(!) sub-module ldap.syncrepl implementing syncrepl consumer (see RFC 4533, thanks to Chris Mikkelson) Doc/ * Cleaned up rst files * Added missing classes ---------------------------------------------------------------- Released 2.4.3 2011-07-23 Changes since 2.4.2: Lib/ * Mostly corrected/updated __doc__ strings Doc/ * Corrected rst files * Added missing modules, functions, classes, methods, parameters etc. at least as auto-generated doc ---------------------------------------------------------------- Released 2.4.2 2011-07-21 Changes since 2.4.1: Lib/ Logging: * pprint.pformat() is now used when writing method/function arguments to the trace log ldap.schema.subentry: * SubSchema.__init__() now has new key-word argument check_uniqueness which enables checking whether OIDs are unique in the subschema subentry * Code-cleaning: consequent use of method SubSchema.getoid() instead of accessing SubSchema.name2oid directly. * SubSchema.getoid() and SubSchema.getoid() now have key-word argument raise_keyerror=0 and raise KeyError with appropriate description. ---------------------------------------------------------------- Released 2.4.1 2011-07-05 Changes since 2.4.0: Modules: * New LDAP option OPT_X_TLS_PACKAGE available in OpenLDAP 2.4.26+ to determine the name of the SSL/TLS package OpenLDAP was built with Lib/ * ldap.modlist.modifyModlist(): New key-word argument case_ignore_attr_types used to define attribute types for which comparison of old and new values should be case-insensitive * Minor changes to which data is sent to debug output for various trace levels * Now tag [1] is used in ldap.extop.dds.RefreshResponse in compliance with RFC 2589 (fix available for OpenLDAP ITS#6886) * New sub-module ldap.controls.sessiontrack implements request control as described in draft-wahl-ldap-session (needs pyasn1_modules) ---------------------------------------------------------------- Released 2.4.0 2011-06-02 Changes since 2.3.13: * OpenLDAP 2.4.11+ required to build * Support for extracting LDAPv3 extended controls in LDAP_RES_SEARCH_ENTRY responses (see SF#2829057, thanks to Rich) * Generic support for LDAPv3 extended operations (thanks to Rich) Lib/ * new class API in ldap.controls, not backwards-compatible! * new sub-modules for ldap.controls, some require pyasn1 and pyasn1_modules * New methods LDAPObject.result4() and LDAPObject.extop_result() * New (optional) class ldap.controls.AssertionControl * New helper module ldap.logger contains file-like object which sends trace messages to logging.log() * Removed non-functional method LDAPObject.set_cache_options() * Removed unused dictionary ldap.controls.knownLDAPControls Modules/ * ldapcontrol.c: Fixed encode_assertion_control() and function is no longer hidden behind ifdef-statement ---------------------------------------------------------------- Released 2.3.13 2011-02-19 Changes since 2.3.12: Modules/ * Correct #ifdef-statement for LDAP_OPT_X_TLS_CRLFILE in constants.c fixes build with older OpenLDAP libs * Support for LDAP_OPT_DEFBASE (see SF#3072016, thanks to Johannes) ---------------------------------------------------------------- Released 2.3.12 2010-08-05 Changes since 2.3.11: Lib/ * Removed tabs from various modules to make things work with python -tt. * Quick fix to ldif.is_dn() to let multi-valued RDNs pass as valid. Is too liberal in some corner-cases though... * Fix to ldif.is_dn() to allow dashes in attribute type (see SF#3020292) * ldap.open() now outputs a deprecation warning * module-wide locking is now limited to calling _ldap.initialize(). Still ldap.functions._ldap_function_call() is used to wrap all calls for writing debug log. Modules/ * New LDAP options available in OpenLDAP 2.4.18+ supported in LDAPObject.get/set_option(): ldap.OPT_X_KEEPALIVE_IDLE, ldap.OPT_X_KEEPALIVE_PROBES, ldap.OPT_X_KEEPALIVE_INTERVAL, ldap.OPT_X_TLS_CRLCHECK, ldap.OPT_X_TLS_CRLFILE Doc/ * Various small updates/improvements ---------------------------------------------------------------- Released 2.3.11 2010-02-26 Changes since 2.3.10: Lib/ * Fixed LDAP URL parsing with four ? but no real extensions * ldap.ldapobject.LDAPObject.rename_s() now also accepts arguments serverctrls and clientctrls * Removed untested and undocumented class ldap.ldapobject.SmartLDAPObject * Removed broken method ldap.ldapobject.LDAPObject.manage_dsa_it() Modules/ * Make use of LDAP_OPT_X_TLS_NEWCTX only if available in OpenLDAP libs used for the build * Fixed #ifdef-statements for OPT_X_TLS_PROTOCOL_MIN Doc/ * Some updates and corrections regarding description of use of LDAPv3 controls * Some more descriptions for constants * Removed comments related to old LaTeX-based documentation system ---------------------------------------------------------------- Released 2.3.10 2009-10-30 Changes since 2.3.9: Lib/ * The diagnosticMessage returned by a server is written to the trace output also for successful operations. * Fixed handling of LDAP URL extensions with implicit value None which are mapped to class attributes of LDAPUrl. * Fixed handling of LDAP URLs with ? being part of extensions. * Fixed exceptions raised by get_option/set_option (SF#1964993) * ldap.functions: Fixed import trace-related variables from base-module ldap * Fixed ldap.resiter missing in RPMs built with python setup.py bdist_rpm * Fix in class ldap.schema.models.SchemaElement: repr() was liberally used in methods key_attr() and key_list() to enclose values in quotes. Modules/ * Changed internal API List_to_LDAPControls() to LDAPControls_from_object() * Supported was added for retrieving the SASL username during SASL bind with ldap_get_option(LDAP_OPT_X_SASL_USERNAME) if available in libldap. * New LDAP option constant ldap.OPT_X_TLS_NEWCTX supported in LDAPObject.set_option() * New LDAP option constants supported in LDAPObject.get/set_option(): ldap.OPT_X_TLS_PROTOCOL_MIN, ldap.OPT_CONNECT_ASYNC, ldap.OPT_X_TLS_DHFILE * Fixed setting _ldap.OPT_ON and _ldap.OPT_OFF * l_ldap_result3(): controls are now parsed for all response types (SF#2829057) Doc/ * Added example for ldap.resiter ---------------------------------------------------------------- Released 2.3.9 2009-07-26 Changes since 2.3.8: Lib/ * All modules (ldap, ldif, dsml and ldapurl) have common version number now * Non-exported function ldif.needs_base64() was abandoned and is now implemented as method LDIFWriter._needs_base64_encoding(). This allows sub-classes of LDIFWriter to implement determining whether attribute values have to be base64-encoded in a different manner and is the same approach like in class dsml.DSMLWriter. * LDAPUrlExtension._parse() now gracefully handles LDAP URL extensions without explicit exvalue as being set with implicit value None. Modules/ * New LDAP option constant ldap.OPT_X_SASL_NOCANON supported in LDAPObject.get/set_option() ---------------------------------------------------------------- Released 2.3.8 2009-04-30 Changes since 2.3.7: Lib/ * ldap.schema.models: More fault-tolerant parsing of SYNTAX in AttributeTypeDescription * ldap.schema.tokenizer.split_tokens(): More tolerant parsing of items separated only with a DOLLAR without surrounding white-spaces (because WSP is declared as zero or more spaces in RFC 4512) ---------------------------------------------------------------- Released 2.3.7 2009-04-09 Changes since 2.3.6: Lib/ * urllib.quote() is now used in LDAPUrlExtension.unparse() to quote all special URL characters in extension values Modules/ * Fixed ldapcontrol.c not to raise ldap.ENCODING_ERROR in function encode_rfc2696() on 64-bit systems * Fixed seg fault if error code in a LDAP response was outside the known error codes and could not be mapped to a specific exception class (thanks to Sean) * errors.c: LDAP_ERROR_MAX set to LDAP_PROXIED_AUTHORIZATION_DENIED if available in OpenLDAP header * new exception class ldap.PROXIED_AUTHORIZATION_DENIED if available in OpenLDAP header * Fixed functions.c not to raise ldap.ENCODING_ERROR in function l_ldap_str2dn() on 64-bit systems (see SF#2725356) ---------------------------------------------------------------- Released 2.3.6 2009-02-22 Changes since 2.3.5: Lib/ * Importing ldap.str2dn() which directly imported _ldap.str2dn() is prohibited now (see SF#2181141) Modules/ * get_option(): Added support for reading more SASL options. (OPT_X_SASL_MECH, OPT_X_SASL_REALM, OPT_X_SASL_AUTHCID and OPT_X_SASL_AUTHZID) * Added some explicit type casts to fix issues while building with SunStudio * Fixed compiling issue with GCC 4.4 (see SF#2555793, thanks to Matej and Martin) Doc/ * Clarified not to use ldap_get_dn() directly * Fixed description of ldap.SASL_AVAIL and ldap.TLS_AVAIL (see SF#2555804, thanks to Matej and Martin) ---------------------------------------------------------------- Released 2.3.5 2008-07-06 Changes since 2.3.4: Lib/ * Fixed methods ldap.cidict.__contains__() and ldap.schema.models.Entry.__contains__() * FWIW method LDAPObject.cancel_s() returns a result now * Fixed ldap.schema.models.NameForm: Class attribute oc is now of type string, not tuple to be compliant with RFC 4512 ---------------------------------------------------------------- Released 2.3.4 2008-03-29 Changes since 2.3.3: Modules/ * Fixed seg fault when calling LDAPObject.get_option() (see SF#1926507, thanks to Matej) ---------------------------------------------------------------- Released 2.3.3 2008-03-26 Changes since 2.3.2: Fixed backward-compability when building with OpenLDAP 2.3.x libs. ---------------------------------------------------------------- Released 2.3.2 2008-03-26 Changes since 2.3.1: Lib/ * ldap.dn.escape_dn_chars() now really adheres to RFC 4514 section 2.4 by escaping null characters and a space occurring at the beginning of the string * New method ldap.cidict.cidict.__contains__() * ldap.dn.explode_dn() and ldap.dn.explode_rdn() have a new optional key-word argument flags which is passed to ldap.dn.str2dn(). Modules/ * Removed unused OPT_PRIVATE_EXTENSION_BASE from constants.c Doc/ * Various additions, updates, polishing (thanks to James). ---------------------------------------------------------------- Released 2.3.1 2007-07-25 Changes since 2.3.0: * Support for setuptools (building .egg, thanks to Torsten) * Support for matched values control (RFC 3876, thanks to Andreas) Lib/ * Fixed ldif (see SF#1709111, thanks to Dmitry) * ldap.schema.models: SUP now separated by $ (method __str__() of classes AttributeType, ObjectClass and DITStructureRule, thanks to Stefan) Modules/ * Added constant MOD_INCREMENT to support modify+increment extension (see RFC 4525, thanks to Andreas) ---------------------------------------------------------------- Released 2.3.0 2007-03-27 Changes since 2.2.1: * OpenLDAP 2.3+ required now to build. * Added support for Cancel operation ext. op. if supported in OpenLDAP API of the libs used for the build. Modules/ * Removed deprecated code for setting options by name * Added l_ldap_cancel() * Some modifications related to PEP 353 for Python 2.5 on 64-bit platforms (see SF#1467529, thanks to Matej) * Added new function l_ldap_str2dn(), removed functions l_ldap_explode_dn() and l_ldap_explode_rdn() (see SF#1657848, thanks to David) Lib/ * Added method ldapobject.LDAPObject.cancel() * ldap.schema.subentry.urlfetch() now can do non-anonymous simple bind if the LDAP URL provided contains extensions 'bindname' and 'X-BINDPW'. (see SF#1589206) * ldap.filter.escape_filter_chars() has new a key-word argument escape_mode now which defines which chars to be escaped (see SF#1193271). * Various important fixes to ldapobject.ReconnectLDAPObject * Moved all DN-related functions to sub-module ldap.dn, import them in ldap.functions for backward compability * ldap.dn.explode_dn() and ldap.dn.explode_rdn() use the new wrapper function ldap.dn.str2dn() (related to SF#1657848) * changetype issue partially fixed (see SF#1683746) ---------------------------------------------------------------- Released 2.2.1 2006-11-15 Changes since 2.2.0: Modules/ * Fix for Python 2.5 free(): invalid pointer (see SF#1575329) * passwd() accepts None for arguments user, oldpw, newpw (see SF#1440151) Lib/ * ldif.LDIFWriter.unparse() now accepts instances of derived dict and list classes (see SF#1489898) ---------------------------------------------------------------- Released 2.2.0 2006-04-10 Changes since 2.0.11: * OpenLDAP 2.2+ required now to build. Modules/ * Dropped all occurences of '#ifdef #LDAP_VENDOR_VERSION'. * Fixed wrong tuple size in l_ldap_result3() (see SF#1368108) * Fixed get_option(ldap.OPT_API_INFO) (see SF#1440165) * Fixed memory leak in l_ldap_result3() when all=0 (see SF#1457325) * Fixed memory leak in l_ldap_result3() in error cases (see SF#1464085) Lib/ * Fixed ldap.schema.models.DITStructureRule.__str__() to separate SUP rule-ids with a single space instead of ' $ ' * Fixed ldap.async.Dict * Added ldap.async.IndexedDict * ldap.schema.subentry.SubSchema.attribute_types() has new key-word argument ignore_dit_content_rule ---------------------------------------------------------------- Released 2.0.11 2005-11-07 Changes since 2.0.10: Lib/ * Class ldap.ldapobject.LDAPObject: Each method returns a result now * Class ldap.ldapobject.ReconnectLDAPObject: Some methods called the wrong methods of LDAPObject. Fixed. * Added new class ldap.async.Dict * Slightly cleaned up ldap.schema.subentry.attribute_types() * New sub-module ldap.resiter which simply provides a mix-in class for ldap.ldapobject.LDAPObject with a generator method allresults(). Obviously this only works with Python 2.3+. And it's still experimental. ---------------------------------------------------------------- Released 2.0.10 2005-09-23 Changes since 2.0.9: Lib/ * Switched back to old implementation of ldap.schema.tokenizer.split_tokens() since the new one had a bug which deletes the spaces from DESC * ldap.INSUFFICIENT_ACCESS is now ignored in ldap.ldapobject.LDAPObject.search_subschemasubentry_s() ---------------------------------------------------------------- Released 2.0.9 2005-07-28 Changes since 2.0.8: Modules/ * Removed __doc__ strings from ldapcontrol.c to "fix" build problems with Python versions 2.2 and earlier. ---------------------------------------------------------------- Released 2.0.8 2005-06-22 at Linuxtag 2005, Karlsruhe, Germany Changes since 2.0.7: * Preliminary support for receiving LDAP controls added. Contributor: - Andreas Ames Lib/ - Added classes in module ldif to ldif.__all__ to fix from ldif import * - Removed BitString syntax from ldap.schema.models.NOT_HUMAN_READABLE_LDAP_SYNTAXES since the LDAP encoding is in fact human-readable - ldapurl.LDAPUrlExtension.unparse() outputs empty string if LDAPUrlExtension.exvalue is None - Added ldap.controls.SimplePagedResultsControl ---------------------------------------------------------------- Released 2.0.7 2005-04-29 Changes since 2.0.6: * Added preliminary support for sending LDAP controls with a request. Contributors: - Deepak Giridharagopal - Ingo Steuwer (Receiving controls in LDAP results still not supported.) Modules: * LDAPObject.c: removed l_ldap_manage_dsa_it() * LDAPObject.c: Added missing #ifdef around l_ldap_passwd() for compability with older OpenLDAP libs. Lib/ * New algorithm in ldap.schema.tokenizer.split_tokens() contributed by Wido Depping which is more robust when parsing very broken schema elements (e.g. Oracle's OID). * Fixed argument list (position of timeout) when calling LDAPObject.search_ext_s() from search_st() and search_s(). * LDAPObject.search_ext_s() correctly calls search_ext_s() now. * Re-implemented LDAPObject.manage_dsa_it() without calling _ldap. ---------------------------------------------------------------- Released 2.0.6 2004-12-03 Changes since 2.0.5: Lib/ * Added sub-module ldap.dn * Added function ldap.dn.escape_dn_chars() * Special check when implicitly setting SUP 'top' to structural object classes without SUP defined to avoid a loop in the super class chain. ---------------------------------------------------------------- Released 2.0.5 2004-11-11 Changes since 2.0.4: Some small improvements for SASL: The noisy output during SASL bind is avoided now. Interaction with output on stderr can be enabled by the calling application by explicitly defining SASL flags. Removed obsolete directory Win32/. Lib/ * Make sure that ldap.sasl.sasl.cb_value_dict is a dictionary even when the caller passes in None to argument cb_value_dict * Added new key-word arg sasl_flags to method LDAPObject.sasl_interactive_bind_s() Modules/ * l_ldap_sasl_interactive_bind_s(): New key-word arg sasl_flags passed to ldap_sasl_interactive_bind_s() ---------------------------------------------------------------- Released 2.0.4 2004-10-27 Changes since 2.0.3: Modules/ * Applied some fixes for 64-bit platforms to LDAPObject.c * Constants ldap.TLS_AVAIL and ldap.SASL_AVAIL will indicate whether python-ldap was built with support for SSL/TLS and/or SASL setup.py and Modules/ * Applied some fixes for building under Win32 ---------------------------------------------------------------- Released 2.0.3 2004-10-06 Changes since 2.0.2: * Added support for LDAP Password Modify Extended Operation (see RFC 3062) Demo/: * Added passwd_ext_op.py Modules/ * Added l_ldap_passwd() in LDAPObject.c Lib/ * Added methods passwd() and passwd_s() to ldap.ldapobject.LDAPObject ---------------------------------------------------------------- Released 2.0.2 2004-07-29 Changes since 2.0.1: Modules/ * Fixed detecting appropriate OpenLDAP libs version for determining whether ldap_whoami_s() is available or not. This fixes build problems with OpenLDAP libs 2.1.0 up to 2.1.12. ---------------------------------------------------------------- Released 2.0.1 2004-06-29 Changes since 2.0.0: dsml: * Fixed wrong exception message format string ldap.schema.models: * Fixed Entry.__delitem__() to delete really everything when deleting an attribute dictionary item. ---------------------------------------------------------------- Released 2.0.0 2004-05-18 Changes since 2.0.0pre21: ldif: * Empty records are simply ignored in ldif.LDIFWriter.unparse() Modules/ * New method result2() returns 3-tuple containing the msgid of the outstanding operation. ldap.ldapobject: * New _ldap wrapper method LDAPObject.result2() (see above) which is now used by LDAPObject.result(). ---------------------------------------------------------------- Released 2.0.0pre21 2004-03-29 Changes since 2.0.0pre20: setup.py: * runtime_library_dirs is set Modules/ * (Hopefully) fixed building with OpenLDAP 2.2 libs in errors.c * Removed meaningless repr() function from LDAPObject.c * Removed setting LDAP_OPT_PROTOCOL_VERSION in l_ldap_sasl_bind_s() * Modified string handling via berval instead of *char in l_ldap_compare_ext() makes it possible to compare attribute values with null chars. * Wrapped ldap_sasl_bind() for simple binds instead of ldap_bind() since 1. the latter is marked deprecated and 2. ldap_sasl_bind() allows password credentials with null chars. * Removed unused sources linkedlist.c and linkedlist.h * Function l_ldap_whoami_s() only added if built against OpenLDAP 2.1.x+ libs (should preserve compability with 2.0 libs) ldap.ldapobject: * LDAPObject.bind() only allows simple binds since Kerberos V4 binds of LDAPv2 are not supported anymore. An assert statement was added to make the coder aware of that. * Renamed former LDAPObject.sasl_bind_s() to LDAPObject.sasl_interactive_bind_s() since it wraps OpenLDAP's ldap_sasl_interactive_bind_s() ---------------------------------------------------------------- Released 2.0.0pre20 2004-03-19 Changes since 2.0.0pre19: Modules/ * Removed doc strings from functions.c * Removed probably unused wrapper function l_ldap_dn2ufn() since ldap_dn2ufn() is deprecated in OpenLDAP 2.1+ * Removed wrapper function l_ldap_is_ldap_url(). * Removed macro add_int_r() from constants.c since it caused incompability issues with OpenLDAP 2.2 libs (Warning: all result types are Integers now! Use the constants!) * New wrapper function l_ldap_whoami_s() ldap.ldapobject: * New wrapper method LDAPObject.whoami_s() ldap.functions: * Removed is_ldap_url(). The more general function ldapurl.isLDAPUrl() should be used instead. ldap.sasl: * Added class cram_md5 (for SASL mech CRAM-MD5) ldap.async: * Use constants for search result types (see note about add_int_r() above). ---------------------------------------------------------------- Released 2.0.0pre19 2004-01-22 Changes since 2.0.0pre18: Modules/ * LDAPObject.c: Most deprecated functions of OpenLDAP C API are not used anymore. * functions.c: Removed unused default_ldap_port(). * constants.c: Removed unused or silly constants AUTH_KRBV4, AUTH_KRBV41, AUTH_KRBV42, URL_ERR_BADSCOPE, URL_ERR_MEM * errors.c: Fixed building with OpenLDAP 2.2.x (errors caused by negative error constants in ldap.h) ldap.ldapobject.LDAPObject: * Removed unused wrapper methods uncache_entry(), uncache_request(), url_search(), url_search_st() and url_search_s() * New wrapper methods for all the _ext() methods in _ldap.LDAPObject. ldap.modlist: * Some performance optimizations and simplifications in function modifyModlist() ---------------------------------------------------------------- Released 2.0.0pre18 2003-12-09 Changes since 2.0.0pre17: ldap.ldapobject: * Fixed missing ldap._ldap_function_call() in ReconnectLDAPObject.reconnect() ---------------------------------------------------------------- Released 2.0.0pre17 2003-12-03 Changes since 2.0.0pre16: ldap.functions: * Fixed ImportError when running python -O ---------------------------------------------------------------- Released 2.0.0pre16 2003-12-02 Changes since 2.0.0pre15: Modules/ * Removed definition of unused constant RES_EXTENDED_PARTIAL since the corresponding symbol LDAP_RES_EXTENDED_PARTIAL seems to not be available in OpenLDAP-HEAD (pre 2.2) anymore. All in Lib/ * Fixed some subtle bugs/oddities mentioned by pychecker. dsml: * Renamed DSMLWriter._f to DSMLWriter._output_file * Added wrapper method DSMLWriter.unparse() which simply calls DSMLWriter.writeRecord() ldap.ldapobject: * Simplified LDAPObject.search_subschemasubentry_s() ldap.functions: * Moved ldap._ldap_function_call() into ldap.functions. * apply() is not used anymore since it seems deprecated ldap.async: * Added class DSMLWriter ldap.schema: * Removed unused key-word argument strict from ldap.schema.subentry.SubSchema.attribute_types() * Fixed backward compability issue (for Python prior to 2.2) in ldap.schema.subentry.SubSchema.listall() ---------------------------------------------------------------- Released 2.0.0pre15 2003-11-11 Changes since 2.0.0pre14: Modules/ Follow rule "Always include Python.h first" ldap.schema.subentry: * Added new method SubSchema.get_structural_oc() * Added new method SubSchema.get_applicable_aux_classes() * Methods SubSchema.listall() and SubSchema.tree() have new key-word argument schema_element_filters * Support for DIT content rules in SubSchema.attribute_types() ---------------------------------------------------------------- Released 2.0.0pre14 2003-10-03 Changes since 2.0.0pre13: setup.py: * Some modifications to ease building for Win32 * Added directory Build/ mainly intended for platform-specific examples of setup.cfg * Fixed installing ldap.filter ldap.ldapobject: * Added class attribute LDAPObject.network_timeout mapped to set_option(ldap.OPT_NETWORK_TIMEOUT,..) * LDAPObject.search_ext(): Pass arguments serverctrls,clientctrls to _ldap.search_ext() ldap.sasl: * Added class ldap.sasl.external for handling the SASL mechanism EXTERNAL * Dictionary ldap.sasl.saslmech_handler_class built during import for all the known SASL mechanisms derived from class definitions ldap.schema: * More graceful handling of KeyError in SubSchema.attribute_types() * New method SubSchema.get_inheritedattr() for retrieving inherited class attributes * New method SubSchema.get_inheritedobj() for retrieving a schema element instance including all inherited class attributes ---------------------------------------------------------------- Released 2.0.0pre13 2003-06-02 Changes since 2.0.0pre12: ldap.async: * Checking type of argument writer_obj relaxed in LDIFWriter.__init__() since file-like objects are not necessarily an instance of file. ldap.schema: * ldap.schema.subentry.SubSchema.attribute_types() now correctly handles attribute types without NAME set * If SUP is not defined for a structural object class 'top' is assumed to be the only super-class by default * '_' is now the abstract top node in SubSchema.tree() for all schema element classes since ABSTRACT and AUXILIARY object classes are not derived from 'top' by default ---------------------------------------------------------------- Released 2.0.0pre12 2003-05-27 Changes since 2.0.0pre11: New sub-module ldap.filter: * Added functions escape_filter_chars() and filter_format() ldap.ldapobject: * Trace log writes LDAP URI of connection instead of module name * search_s() passes self.timeout as argument timeout when calling search_ext_s() * Key-word arguments for simple_bind() and simple_bind_s() with defaults for anonymous bind. * LDAPObject.protocol_version is set to LDAPv3 as default (this might make code changes necessary in a real LDAPv2 environment) * Default for key-word argument trace_stack_limit passed to __init__() is 5 * Updated __doc__ strings * Aligned and tested ReconnectLDAPObject and SmartLDAPObject ldap.async: * LDIFWriter uses ldif.LDIFWriter instead of calling function ldif.CreateLDIF * LDIFWriter accepts either file-like object or ldif.LDIFWriter instance as argument for specifying the output ldif: * Abandoned argument all_records of LDIFRecordList.__init__() ldapurl: * urllib.unquote() used instead of urllib.unquote_plus() ---------------------------------------------------------------- Released 2.0.0pre11 2003-05-02 Changes since 2.0.0pre10: ldap.ldapobject: * Cosmetic change: Named argument list for LDAPObject.compare() instead of *args,**kwargs. * Fixed bug in ReconnectLDAPObject._apply_method_s() affecting compability with Python 2.0. The bug was introduced with 2.0.0pre09 by dropping use of apply(). ldap.modlist: * modifyModlist(): Only None is filtered from attribute value lists, '' is preserved as valid attribute value. But filtering applies to old_value and new_value now. ldap.schema: * Zero-length attribute values for schema elements are ignored (needed on e.g. Active Directory) dsml: Added support for parsing and generating DSMLv1. Still experimental though. ---------------------------------------------------------------- Released 2.0.0pre10 2003-04-19 Changes since 2.0.0pre09: ldap.schema: * Emulate BooleanType for compability with Python2.3 in assert statements ---------------------------------------------------------------- Released 2.0.0pre09 2003-04-19 Changes since 2.0.0pre08: Modified setup.py to support Cyrus-SASL 2.x. ldap.ldapobject: * apply() is not used anymore since it seems deprecated * Fixed __setstate__() and __getstate__() of ReconnectLDAPObject ldap.schema: * Completed classes for nameForms, dITStructureRules and dITContentRules ---------------------------------------------------------------- Released 2.0.0pre08 2003-04-11 Changes since 2.0.0pre07: ldap.schema: * For backward compability with Python versions prior to 2.2 Lib/ldap/schema/tokenizer.py and Lib/ldap/schema/models.py use (()) instead of tuple() for creating empty tuples. ---------------------------------------------------------------- Released 2.0.0pre07 2003-04-03 Changes since 2.0.0pre06: LDAPObject.c: * Wrapped OpenLDAP's ldap_search_ext() * Removed empty __doc__ strings * Removed fileno * Removed all stuff related to caching in OpenLDAP libs ldap.ldapobject: * Fixed SASL rebind in ldap.ldapobject.ReconnectLDAPObject * use search_ext() instead ldap_search() * new class attribute timeout for setting a global time-out value for all synchronous operations ldap.schema: * Fixed two typos in ldap.schema.models * Some attempts to improve performance of parser/tokenizer * Completely reworked to have separate OID dictionaries for the different schema element classes * Fixed the Demo/schema*.py to reflect changes to ldap.schema Documentation updates and various __doc__ string modifications. ldapurl: * Removed all Unicode stuff from module ldapurl * Consistent URL encoding in module ldapurl ldif: * Removed ldif.FileWriter * Proper handling of FILL (see RFC 2849) ---------------------------------------------------------------- Released 2.0.0pre06 2002-09-23 Changes since 2.0.0pre05: - Fine-grained locking when linking against libldap_r - New wrapper class ldap.ReconnectLDAPObject - Security fix to module ldapurl - Other fixes and improvements to whole package - LDAPv3 schema support (still somewhat premature and undocumented) ---------------------------------------------------------------- Released 2.0.0pre05 2002-07-20 ---------------------------------------------------------------- Released 2.0.0pre04 2002-02-09 ---------------------------------------------------------------- Released 2.0.0pre02 2002-02-01 ---------------------------------------------------------------- Released 1.10alpha3 2000-09-19 $Id: CHANGES,v 1.291 2012/06/07 18:38:47 stroeder Exp $ python-ldap-2.4.10/setup.py0000644000076400001440000001320111660514461016335 0ustar michaelusers00000000000000""" setup.py - Setup package with the help Python's DistUtils See http://www.python-ldap.org/ for details. $Id: setup.py,v 1.71 2011/10/26 19:42:25 stroeder Exp $ """ has_setuptools = False try: from setuptools import setup, Extension has_setuptools = True except ImportError: from distutils.core import setup, Extension from ConfigParser import ConfigParser import sys,os,string,time ################################################################## # Weird Hack to grab release version of python-ldap from local dir ################################################################## exec_startdir = os.path.dirname(os.path.abspath(sys.argv[0])) package_init_file_name = reduce(os.path.join,[exec_startdir,'Lib','ldap','__init__.py']) f = open(package_init_file_name,'r') s = f.readline() while s: s = string.strip(s) if s[0:11]=='__version__': version = eval(string.split(s,'=')[1]) break s = f.readline() f.close() #-- A class describing the features and requirements of OpenLDAP 2.0 class OpenLDAP2: library_dirs = [] include_dirs = [] extra_compile_args = [] extra_link_args = [] extra_objects = [] libs = ['ldap', 'lber'] defines = [ ] extra_files = [] LDAP_CLASS = OpenLDAP2 #-- Read the [_ldap] section of setup.cfg cfg = ConfigParser() cfg.read('setup.cfg') if cfg.has_section('_ldap'): for name in dir(LDAP_CLASS): if cfg.has_option('_ldap', name): print name + ': ' + cfg.get('_ldap', name) setattr(LDAP_CLASS, name, string.split(cfg.get('_ldap', name))) for i in range(len(LDAP_CLASS.defines)): LDAP_CLASS.defines[i]=((LDAP_CLASS.defines[i],None)) for i in range(len(LDAP_CLASS.extra_files)): destdir, origfiles = string.split(LDAP_CLASS.extra_files[i], ':') origfileslist = string.split(origfiles, ',') LDAP_CLASS.extra_files[i]=(destdir, origfileslist) #-- Let distutils/setuptools do the rest name = 'python-ldap' # Python 2.3.6+ and setuptools are needed to build eggs, so # let's handle setuptools' additional keyword arguments to # setup() in a fashion that doesn't break compatibility to # distutils. This still allows 'normal' builds where either # Python > 2.3.5 or setuptools (or both ;o) are not available. kwargs = dict() if has_setuptools: kwargs = dict( include_package_data = True, install_requires = ['setuptools'], zip_safe = False ) setup( #-- Package description name = name, version = version, description = 'Python modules for implementing LDAP clients', long_description = """python-ldap: python-ldap provides an object-oriented API to access LDAP directory servers from Python programs. Mainly it wraps the OpenLDAP 2.x libs for that purpose. Additionally the package contains modules for other LDAP-related stuff (e.g. processing LDIF, LDAPURLs, LDAPv3 schema, LDAPv3 extended operations and controls, etc.). """, author = 'python-ldap project', author_email = 'python-ldap@python.org', url = 'http://www.python-ldap.org/', download_url = 'http://pypi.python.org/pypi/python-ldap/', classifiers = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'Operating System :: OS Independent', 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX', 'Programming Language :: C', 'Programming Language :: Python', 'Topic :: Database', 'Topic :: Internet', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP', 'License :: OSI Approved :: Python Software Foundation License', ], license = 'Python style', #-- C extension modules ext_modules = [ Extension( '_ldap', [ 'Modules/LDAPObject.c', 'Modules/ldapcontrol.c', 'Modules/common.c', 'Modules/constants.c', 'Modules/errors.c', 'Modules/functions.c', 'Modules/schema.c', 'Modules/ldapmodule.c', 'Modules/message.c', 'Modules/version.c', 'Modules/options.c', 'Modules/berval.c', ], libraries = LDAP_CLASS.libs, include_dirs = ['Modules'] + LDAP_CLASS.include_dirs, library_dirs = LDAP_CLASS.library_dirs, extra_compile_args = LDAP_CLASS.extra_compile_args, extra_link_args = LDAP_CLASS.extra_link_args, extra_objects = LDAP_CLASS.extra_objects, runtime_library_dirs = (not sys.platform.startswith("win"))*LDAP_CLASS.library_dirs, define_macros = LDAP_CLASS.defines + \ ('ldap_r' in LDAP_CLASS.libs or 'oldap_r' in LDAP_CLASS.libs)*[('HAVE_LIBLDAP_R',None)] + \ ('sasl' in LDAP_CLASS.libs or 'sasl2' in LDAP_CLASS.libs or 'libsasl' in LDAP_CLASS.libs)*[('HAVE_SASL',None)] + \ ('ssl' in LDAP_CLASS.libs and 'crypto' in LDAP_CLASS.libs)*[('HAVE_TLS',None)] + \ [('LDAPMODULE_VERSION', version)] ), ], #-- Python "stand alone" modules py_modules = [ 'ldapurl', 'ldif', 'dsml', 'ldap', 'ldap.async', 'ldap.controls', 'ldap.controls.libldap', 'ldap.controls.ppolicy', 'ldap.controls.psearch', 'ldap.controls.readentry', 'ldap.controls.sessiontrack', 'ldap.controls.simple', 'ldap.cidict', 'ldap.dn', 'ldap.extop', 'ldap.extop.dds', 'ldap.filter', 'ldap.functions', 'ldap.ldapobject', 'ldap.logger', 'ldap.modlist', 'ldap.resiter', 'ldap.sasl', 'ldap.schema', 'ldap.schema.models', 'ldap.schema.subentry', 'ldap.schema.tokenizer', 'ldap.syncrepl', ], package_dir = {'': 'Lib',}, data_files = LDAP_CLASS.extra_files, **kwargs ) python-ldap-2.4.10/PKG-INFO0000644000076400001440000000263711764172736015745 0ustar michaelusers00000000000000Metadata-Version: 1.1 Name: python-ldap Version: 2.4.10 Summary: Python modules for implementing LDAP clients Home-page: http://www.python-ldap.org/ Author: python-ldap project Author-email: python-ldap@python.org License: Python style Download-URL: http://pypi.python.org/pypi/python-ldap/ Description: python-ldap: python-ldap provides an object-oriented API to access LDAP directory servers from Python programs. Mainly it wraps the OpenLDAP 2.x libs for that purpose. Additionally the package contains modules for other LDAP-related stuff (e.g. processing LDIF, LDAPURLs, LDAPv3 schema, LDAPv3 extended operations and controls, etc.). Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Operating System :: OS Independent Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Topic :: Database Classifier: Topic :: Internet Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP Classifier: License :: OSI Approved :: Python Software Foundation License python-ldap-2.4.10/MANIFEST.in0000644000076400001440000000035511273113550016361 0ustar michaelusers00000000000000include MANIFEST.in Makefile CHANGES INSTALL LICENCE README TODO PKG-INFO include Modules/*.c Modules/*.h Modules/LICENSE recursive-include Build *.cfg* recursive-include Lib *.py recursive-include Demo *.py recursive-include Tests *.py python-ldap-2.4.10/setup.cfg0000644000076400001440000000103011764172736016453 0ustar michaelusers00000000000000[_ldap] library_dirs = /opt/openldap-RE24/lib /usr/lib include_dirs = /opt/openldap-RE24/include /usr/include/sasl /usr/include defines = HAVE_SASL HAVE_TLS HAVE_LIBLDAP_R extra_compile_args = extra_objects = libs = ldap_r [install] compile = 1 optimize = 1 [bdist_rpm] provides = python-ldap requires = python libldap-2_4 vendor = python-ldap project packager = Michael Ströder distribution_name = openSUSE 11.x release = 1 doc_files = CHANGES README INSTALL TODO Demo/ [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 python-ldap-2.4.10/LICENCE0000644000076400001440000000113607542145452015620 0ustar michaelusers00000000000000 The python-ldap package is distributed under Python-style license. Standard disclaimer: This software is made available by the author(s) to the public for free and "as is". All users of this free software are solely and entirely responsible for their own choice and use of this software for their own purposes. By using this software, each user agrees that the author(s) shall not be liable for damages of any kind in relation to its use or performance. The author(s) do not warrant that this software is fit for any purpose. $Id: LICENCE,v 1.1 2002/09/18 18:51:22 stroeder Exp $ python-ldap-2.4.10/Lib/0000755000076400001440000000000011764172736015346 5ustar michaelusers00000000000000python-ldap-2.4.10/Lib/python_ldap.egg-info/0000755000076400001440000000000011764172736021361 5ustar michaelusers00000000000000python-ldap-2.4.10/Lib/python_ldap.egg-info/requires.txt0000644000076400001440000000001211764172735023751 0ustar michaelusers00000000000000setuptoolspython-ldap-2.4.10/Lib/python_ldap.egg-info/SOURCES.txt0000644000076400001440000000567111764172735023255 0ustar michaelusers00000000000000.cvsignore CHANGES INSTALL LICENCE MANIFEST.in PKG-INFO README TODO setup.cfg setup.py Build/setup.cfg.mingw Build/setup.cfg.suse-linux Build/setup.cfg.win32 Build/build-openbsd/Makefile Build/build-openbsd/pkg/COMMENT Build/build-openbsd/pkg/DESCR Build/build-openbsd/pkg/PLIST Demo/.cvsignore Demo/initialize.py Demo/ldapcontrols.py Demo/matchedvalues.py Demo/ms_ad_bind.py Demo/options.py Demo/page_control.py Demo/paged_search_ext_s.py Demo/passwd_ext_op.py Demo/pickle_ldapobject.py Demo/reconnect.py Demo/rename.py Demo/resiter.py Demo/sasl_bind.py Demo/schema.py Demo/schema_tree.py Demo/simple.py Demo/simplebrowse.py Demo/Lib/.cvsignore Demo/Lib/ldap/.cvsignore Demo/Lib/ldap/async/.cvsignore Demo/Lib/ldap/async/deltree.py Demo/Lib/ldap/async/ldifwriter.py Demo/Lib/ldap/async/sizelimit.py Demo/Lib/ldapurl/.cvsignore Demo/Lib/ldapurl/urlsearch.py Demo/Lib/ldif/.cvsignore Demo/Lib/ldif/ldifcopy.py Demo/pyasn1/README Demo/pyasn1/dds.py Demo/pyasn1/ppolicy.py Demo/pyasn1/psearch.py Demo/pyasn1/readentrycontrol.py Demo/pyasn1/sessiontrack.py Demo/pyasn1/syncrepl.py Doc/.cvsignore Doc/Makefile Doc/conf.py Doc/dsml.rst Doc/index.rst Doc/installing.rst Doc/ldap-async.rst Doc/ldap-controls.rst Doc/ldap-dn.rst Doc/ldap-extop.rst Doc/ldap-filter.rst Doc/ldap-modlist.rst Doc/ldap-resiter.rst Doc/ldap-schema.rst Doc/ldap-syncrepl.rst Doc/ldap.rst Doc/ldapurl.rst Doc/ldif.rst Lib/.cvsignore Lib/dsml.py Lib/ldapurl.py Lib/ldif.py Lib/ldap/.cvsignore Lib/ldap/__init__.py Lib/ldap/async.py Lib/ldap/cidict.py Lib/ldap/dn.py Lib/ldap/filter.py Lib/ldap/functions.py Lib/ldap/ldapobject.py Lib/ldap/logger.py Lib/ldap/modlist.py Lib/ldap/resiter.py Lib/ldap/sasl.py Lib/ldap/syncrepl.py Lib/ldap/controls/__init__.py Lib/ldap/controls/libldap.py Lib/ldap/controls/ppolicy.py Lib/ldap/controls/psearch.py Lib/ldap/controls/pwdpolicy.py Lib/ldap/controls/readentry.py Lib/ldap/controls/sessiontrack.py Lib/ldap/controls/simple.py Lib/ldap/extop/__init__.py Lib/ldap/extop/dds.py Lib/ldap/schema/.cvsignore Lib/ldap/schema/__init__.py Lib/ldap/schema/models.py Lib/ldap/schema/subentry.py Lib/ldap/schema/tokenizer.py Lib/python_ldap.egg-info/PKG-INFO Lib/python_ldap.egg-info/SOURCES.txt Lib/python_ldap.egg-info/dependency_links.txt Lib/python_ldap.egg-info/not-zip-safe Lib/python_ldap.egg-info/requires.txt Lib/python_ldap.egg-info/top_level.txt Modules/.cvsignore Modules/LDAPObject.c Modules/LDAPObject.h Modules/berval.c Modules/berval.h Modules/common.c Modules/common.h Modules/constants.c Modules/constants.h Modules/errors.c Modules/errors.h Modules/functions.c Modules/functions.h Modules/ldapcontrol.c Modules/ldapcontrol.h Modules/ldapmodule.c Modules/message.c Modules/message.h Modules/options.c Modules/options.h Modules/schema.c Modules/schema.h Modules/version.c Modules/version.h Tests/runtests.sh Tests/search.py Tests/slapd.py Tests/t_cext.py Tests/t_ldapurl.py Tests/t_search.py Tests/Lib/test_ldapurl.py Tests/Lib/ldap/test_modlist.py Tests/Lib/ldap/schema/test_tokenizer.pypython-ldap-2.4.10/Lib/python_ldap.egg-info/top_level.txt0000644000076400001440000000003511764172735024110 0ustar michaelusers00000000000000dsml ldapurl ldap _ldap ldif python-ldap-2.4.10/Lib/python_ldap.egg-info/dependency_links.txt0000644000076400001440000000000111764172735025426 0ustar michaelusers00000000000000 python-ldap-2.4.10/Lib/python_ldap.egg-info/PKG-INFO0000644000076400001440000000263711764172735022465 0ustar michaelusers00000000000000Metadata-Version: 1.1 Name: python-ldap Version: 2.4.10 Summary: Python modules for implementing LDAP clients Home-page: http://www.python-ldap.org/ Author: python-ldap project Author-email: python-ldap@python.org License: Python style Download-URL: http://pypi.python.org/pypi/python-ldap/ Description: python-ldap: python-ldap provides an object-oriented API to access LDAP directory servers from Python programs. Mainly it wraps the OpenLDAP 2.x libs for that purpose. Additionally the package contains modules for other LDAP-related stuff (e.g. processing LDIF, LDAPURLs, LDAPv3 schema, LDAPv3 extended operations and controls, etc.). Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Operating System :: OS Independent Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Topic :: Database Classifier: Topic :: Internet Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP Classifier: License :: OSI Approved :: Python Software Foundation License python-ldap-2.4.10/Lib/python_ldap.egg-info/not-zip-safe0000644000076400001440000000000111612065006023566 0ustar michaelusers00000000000000 python-ldap-2.4.10/Lib/ldapurl.py0000644000076400001440000002675311764172710017370 0ustar michaelusers00000000000000""" ldapurl - handling of LDAP URLs as described in RFC 4516 See http://www.python-ldap.org/ for details. \$Id: ldapurl.py,v 1.58 2012/06/07 18:40:59 stroeder Exp $ Python compability note: This module only works with Python 2.0+ since 1. string methods are used instead of module string and 2. list comprehensions are used. """ __version__ = '2.4.10' __all__ = [ # constants 'SEARCH_SCOPE','SEARCH_SCOPE_STR', 'LDAP_SCOPE_BASE','LDAP_SCOPE_ONELEVEL','LDAP_SCOPE_SUBTREE', # functions 'isLDAPUrl', # classes 'LDAPUrlExtension','LDAPUrlExtensions','LDAPUrl' ] import UserDict from urllib import quote,unquote LDAP_SCOPE_BASE = 0 LDAP_SCOPE_ONELEVEL = 1 LDAP_SCOPE_SUBTREE = 2 SEARCH_SCOPE_STR = {None:'',0:'base',1:'one',2:'sub'} SEARCH_SCOPE = { '':None, # the search scope strings defined in RFC2255 'base':LDAP_SCOPE_BASE, 'one':LDAP_SCOPE_ONELEVEL, 'sub':LDAP_SCOPE_SUBTREE, } # Some widely used types StringType = type('') TupleType=type(()) def isLDAPUrl(s): """ Returns 1 if s is a LDAP URL, 0 else """ s_lower = s.lower() return \ s_lower.startswith('ldap://') or \ s_lower.startswith('ldaps://') or \ s_lower.startswith('ldapi://') def ldapUrlEscape(s): """Returns URL encoding of string s""" return quote(s).replace(',','%2C').replace('/','%2F') class LDAPUrlExtension: """ Class for parsing and unparsing LDAP URL extensions as described in RFC 4516. Usable class attributes: critical Boolean integer marking the extension as critical extype Type of extension exvalue Value of extension """ def __init__(self,extensionStr=None,critical=0,extype=None,exvalue=None): self.critical = critical self.extype = extype self.exvalue = exvalue if extensionStr: self._parse(extensionStr) def _parse(self,extension): extension = extension.strip() if not extension: # Don't parse empty strings self.extype,self.exvalue = None,None return self.critical = extension[0]=='!' if extension[0]=='!': extension = extension[1:].strip() try: self.extype,self.exvalue = extension.split('=',1) except ValueError: # No value, just the extype self.extype,self.exvalue = extension,None else: self.exvalue = unquote(self.exvalue.strip()) self.extype = self.extype.strip() def unparse(self): if self.exvalue is None: return '%s%s' % ('!'*(self.critical>0),self.extype) else: return '%s%s=%s' % ( '!'*(self.critical>0), self.extype,quote(self.exvalue or '') ) def __str__(self): return self.unparse() def __repr__(self): return '<%s.%s instance at %s: %s>' % ( self.__class__.__module__, self.__class__.__name__, hex(id(self)), self.__dict__ ) def __eq__(self,other): return \ (self.critical==other.critical) and \ (self.extype==other.extype) and \ (self.exvalue==other.exvalue) def __ne__(self,other): return not self.__eq__(other) class LDAPUrlExtensions(UserDict.UserDict): """ Models a collection of LDAP URL extensions as dictionary type """ def __init__(self,default=None): UserDict.UserDict.__init__(self) for k,v in (default or {}).items(): self[k]=v def __setitem__(self,name,value): """ value Either LDAPUrlExtension instance, (critical,exvalue) or string'ed exvalue """ assert isinstance(value,LDAPUrlExtension) assert name==value.extype self.data[name] = value def values(self): return [ self[k] for k in self.keys() ] def __str__(self): return ','.join(map(str,self.values())) def __repr__(self): return '<%s.%s instance at %s: %s>' % ( self.__class__.__module__, self.__class__.__name__, hex(id(self)), self.data ) def __eq__(self,other): assert isinstance(other,self.__class__),TypeError( "other has to be instance of %s" % (self.__class__) ) return self.data==other.data def parse(self,extListStr): for extension_str in extListStr.strip().split(','): if extension_str: e = LDAPUrlExtension(extension_str) self[e.extype] = e def unparse(self): return ','.join([ v.unparse() for v in self.values() ]) class LDAPUrl: """ Class for parsing and unparsing LDAP URLs as described in RFC 4516. Usable class attributes: urlscheme URL scheme (either ldap, ldaps or ldapi) hostport LDAP host (default '') dn String holding distinguished name (default '') attrs list of attribute types (default None) scope integer search scope for ldap-module filterstr String representation of LDAP Search Filters (see RFC 4515) extensions Dictionary used as extensions store who Maps automagically to bindname LDAP URL extension cred Maps automagically to X-BINDPW LDAP URL extension """ attr2extype = {'who':'bindname','cred':'X-BINDPW'} def __init__( self, ldapUrl=None, urlscheme='ldap', hostport='',dn='',attrs=None,scope=None,filterstr=None, extensions=None, who=None,cred=None ): self.urlscheme=urlscheme self.hostport=hostport self.dn=dn self.attrs=attrs self.scope=scope self.filterstr=filterstr self.extensions=(extensions or LDAPUrlExtensions({})) if ldapUrl!=None: self._parse(ldapUrl) if who!=None: self.who = who if cred!=None: self.cred = cred def __eq__(self,other): return \ self.urlscheme==other.urlscheme and \ self.hostport==other.hostport and \ self.dn==other.dn and \ self.attrs==other.attrs and \ self.scope==other.scope and \ self.filterstr==other.filterstr and \ self.extensions==other.extensions def __ne__(self,other): return not self.__eq__(other) def _parse(self,ldap_url): """ parse a LDAP URL and set the class attributes urlscheme,host,dn,attrs,scope,filterstr,extensions """ if not isLDAPUrl(ldap_url): raise ValueError,'Parameter ldap_url does not seem to be a LDAP URL.' scheme,rest = ldap_url.split('://',1) self.urlscheme = scheme.strip() if not self.urlscheme in ['ldap','ldaps','ldapi']: raise ValueError,'LDAP URL contains unsupported URL scheme %s.' % (self.urlscheme) slash_pos = rest.find('/') qemark_pos = rest.find('?') if (slash_pos==-1) and (qemark_pos==-1): # No / and ? found at all self.hostport = unquote(rest) self.dn = '' return else: if slash_pos!=-1 and (qemark_pos==-1 or (slash_posqemark_pos)): # Question mark separates hostport from rest, DN is assumed to be empty self.hostport = unquote(rest[:qemark_pos]) # Do not eat question mark rest = rest[qemark_pos:] else: raise ValueError,'Something completely weird happened!' paramlist=rest.split('?',4) paramlist_len = len(paramlist) if paramlist_len>=1: self.dn = unquote(paramlist[0]).strip() if (paramlist_len>=2) and (paramlist[1]): self.attrs = unquote(paramlist[1].strip()).split(',') if paramlist_len>=3: scope = paramlist[2].strip() try: self.scope = SEARCH_SCOPE[scope] except KeyError: raise ValueError,"Search scope must be either one of base, one or sub. LDAP URL contained %s" % (repr(scope)) if paramlist_len>=4: filterstr = paramlist[3].strip() if not filterstr: self.filterstr = None else: self.filterstr = unquote(filterstr) if paramlist_len>=5: if paramlist[4]: self.extensions = LDAPUrlExtensions() self.extensions.parse(paramlist[4]) else: self.extensions = None return def applyDefaults(self,defaults): """ Apply defaults to all class attributes which are None. defaults Dictionary containing a mapping from class attributes to default values """ for k in defaults.keys(): if getattr(self,k) is None: setattr(self,k,defaults[k]) def initializeUrl(self): """ Returns LDAP URL suitable to be passed to ldap.initialize() """ if self.urlscheme=='ldapi': # hostport part might contain slashes when ldapi:// is used hostport = ldapUrlEscape(self.hostport) else: hostport = self.hostport return '%s://%s' % (self.urlscheme,hostport) def unparse(self): """ Returns LDAP URL depending on class attributes set. """ if self.attrs is None: attrs_str = '' else: attrs_str = ','.join(self.attrs) scope_str = SEARCH_SCOPE_STR[self.scope] if self.filterstr is None: filterstr = '' else: filterstr = ldapUrlEscape(self.filterstr) dn = ldapUrlEscape(self.dn) if self.urlscheme=='ldapi': # hostport part might contain slashes when ldapi:// is used hostport = ldapUrlEscape(self.hostport) else: hostport = self.hostport ldap_url = '%s://%s/%s?%s?%s?%s' % ( self.urlscheme, hostport,dn,attrs_str,scope_str,filterstr ) if self.extensions: ldap_url = ldap_url+'?'+self.extensions.unparse() return ldap_url def htmlHREF(self,urlPrefix='',hrefText=None,hrefTarget=None): """ Returns a string with HTML link for this LDAP URL. urlPrefix Prefix before LDAP URL (e.g. for addressing another web-based client) hrefText link text/description hrefTarget string added as link target attribute """ assert type(urlPrefix)==StringType, "urlPrefix must be StringType" if hrefText is None: hrefText = self.unparse() assert type(hrefText)==StringType, "hrefText must be StringType" if hrefTarget is None: target = '' else: assert type(hrefTarget)==StringType, "hrefTarget must be StringType" target = ' target="%s"' % hrefTarget return '%s' % ( target,urlPrefix,self.unparse(),hrefText ) def __str__(self): return self.unparse() def __repr__(self): return '<%s.%s instance at %s: %s>' % ( self.__class__.__module__, self.__class__.__name__, hex(id(self)), self.__dict__ ) def __getattr__(self,name): if self.attr2extype.has_key(name): extype = self.attr2extype[name] if self.extensions and \ self.extensions.has_key(extype) and \ not self.extensions[extype].exvalue is None: result = unquote(self.extensions[extype].exvalue) else: return None else: raise AttributeError,"%s has no attribute %s" % ( self.__class__.__name__,name ) return result # __getattr__() def __setattr__(self,name,value): if self.attr2extype.has_key(name): extype = self.attr2extype[name] if value is None: # A value of None means that extension is deleted delattr(self,name) elif value!=None: # Add appropriate extension self.extensions[extype] = LDAPUrlExtension( extype=extype,exvalue=unquote(value) ) else: self.__dict__[name] = value def __delattr__(self,name): if self.attr2extype.has_key(name): extype = self.attr2extype[name] if self.extensions: try: del self.extensions[extype] except KeyError: pass else: del self.__dict__[name] python-ldap-2.4.10/Lib/.cvsignore0000644000076400001440000000002507465025376017343 0ustar michaelusers00000000000000*.pyc *.pyo *.bck *~ python-ldap-2.4.10/Lib/ldap/0000755000076400001440000000000011764172736016266 5ustar michaelusers00000000000000python-ldap-2.4.10/Lib/ldap/extop/0000755000076400001440000000000011764172736017425 5ustar michaelusers00000000000000python-ldap-2.4.10/Lib/ldap/extop/__init__.py0000644000076400001440000000367111612335224021526 0ustar michaelusers00000000000000""" controls.py - support classes for LDAPv3 extended operations See http://www.python-ldap.org/ for details. \$Id: __init__.py,v 1.4 2011/07/22 13:27:02 stroeder Exp $ Description: The ldap.extop module provides base classes for LDAPv3 extended operations. Each class provides support for a certain extended operation request and response. """ from ldap import __version__ class ExtendedRequest: """ Generic base class for a LDAPv3 extended operation request requestName OID as string of the LDAPv3 extended operation request requestValue value of the LDAPv3 extended operation request (here it is the BER-encoded ASN.1 request value) """ def __init__(self,requestName,requestValue): self.requestName = requestName self.requestValue = requestValue def __repr__(self): return '%s(%s,%s)' % (self.__class__.__name__,self.requestName,self.requestValue) def encodedRequestValue(self): """ returns the BER-encoded ASN.1 request value composed by class attributes set before """ return self.requestValue class ExtendedResponse: """ Generic base class for a LDAPv3 extended operation response requestName OID as string of the LDAPv3 extended operation response encodedResponseValue BER-encoded ASN.1 value of the LDAPv3 extended operation response """ def __init__(self,responseName,encodedResponseValue): self.responseName = responseName self.responseValue = self.decodeResponseValue(encodedResponseValue) def __repr__(self): return '%s(%s,%s)' % (self.__class__.__name__,self.responseName,self.responseValue) def decodeResponseValue(self,value): """ decodes the BER-encoded ASN.1 extended operation response value and sets the appropriate class attributes """ return value # Optionally import sub-modules which need pyasn1 et al try: import pyasn1,pyasn1_modules.rfc2251 except ImportError: pass else: from ldap.extop.dds import * python-ldap-2.4.10/Lib/ldap/extop/dds.py0000644000076400001440000000412011603375732020537 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ ldap.extop.dds - Classes for Dynamic Entries extended operations (see RFC 2589) This needs the following software: Python pyasn1 pyasn1-modules python-ldap 2.4+ """ from ldap.extop import ExtendedRequest,ExtendedResponse # Imports from pyasn1 from pyasn1.type import namedtype,univ,tag from pyasn1.codec.der import encoder,decoder from pyasn1_modules.rfc2251 import LDAPDN class RefreshRequest(ExtendedRequest): requestName = '1.3.6.1.4.1.1466.101.119.1' defaultRequestTtl = 86400 class RefreshRequestValue(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType( 'entryName', LDAPDN().subtype( implicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatSimple,0) ) ), namedtype.NamedType( 'requestTtl', univ.Integer().subtype( implicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatSimple,1) ) ), ) def __init__(self,requestName=None,entryName=None,requestTtl=None): self.entryName = entryName self.requestTtl = requestTtl or self.defaultRequestTtl def encodedRequestValue(self): p = self.RefreshRequestValue() p.setComponentByName( 'entryName', LDAPDN(self.entryName).subtype( implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple,0) ) ) p.setComponentByName( 'requestTtl', univ.Integer(self.requestTtl).subtype( implicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatSimple,1) ) ) return encoder.encode(p) class RefreshResponse(ExtendedResponse): responseName = '1.3.6.1.4.1.1466.101.119.1' class RefreshResponseValue(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType( 'responseTtl', univ.Integer().subtype( implicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatSimple,1) ) ) ) def decodeResponseValue(self,value): respValue,_ = decoder.decode(value,asn1Spec=self.RefreshResponseValue()) self.responseTtl = int(respValue.getComponentByName('responseTtl')) return self.responseTtl python-ldap-2.4.10/Lib/ldap/modlist.py0000644000076400001440000001040011573150432020271 0ustar michaelusers00000000000000""" ldap.modlist - create add/modify modlist's See http://www.python-ldap.org/ for details. $Id: modlist.py,v 1.18 2011/06/06 13:07:38 stroeder Exp $ Python compability note: This module is known to work with Python 2.0+ but should work with Python 1.5.2 as well. """ from ldap import __version__ import string,ldap,ldap.cidict def list_dict(l,case_insensitive=0): """ return a dictionary with all items of l being the keys of the dictionary If argument case_insensitive is non-zero ldap.cidict.cidict will be used for case-insensitive string keys """ if case_insensitive: d = ldap.cidict.cidict() else: d = {} for i in l: d[i]=None return d def addModlist(entry,ignore_attr_types=None): """Build modify list for call of method LDAPObject.add()""" ignore_attr_types = list_dict(map(string.lower,(ignore_attr_types or []))) modlist = [] for attrtype in entry.keys(): if ignore_attr_types.has_key(string.lower(attrtype)): # This attribute type is ignored continue # Eliminate empty attr value strings in list attrvaluelist = filter(lambda x:x!=None,entry[attrtype]) if attrvaluelist: modlist.append((attrtype,entry[attrtype])) return modlist # addModlist() def modifyModlist( old_entry,new_entry,ignore_attr_types=None,ignore_oldexistent=0,case_ignore_attr_types=None ): """ Build differential modify list for calling LDAPObject.modify()/modify_s() old_entry Dictionary holding the old entry new_entry Dictionary holding what the new entry should be ignore_attr_types List of attribute type names to be ignored completely ignore_oldexistent If non-zero attribute type names which are in old_entry but are not found in new_entry at all are not deleted. This is handy for situations where your application sets attribute value to '' for deleting an attribute. In most cases leave zero. case_ignore_attr_types List of attribute type names for which comparison will be made case-insensitive """ ignore_attr_types = list_dict(map(string.lower,(ignore_attr_types or []))) case_ignore_attr_types = list_dict(map(string.lower,(case_ignore_attr_types or []))) modlist = [] attrtype_lower_map = {} for a in old_entry.keys(): attrtype_lower_map[string.lower(a)]=a for attrtype in new_entry.keys(): attrtype_lower = string.lower(attrtype) if ignore_attr_types.has_key(attrtype_lower): # This attribute type is ignored continue # Filter away null-strings new_value = filter(lambda x:x!=None,new_entry[attrtype]) if attrtype_lower_map.has_key(attrtype_lower): old_value = old_entry.get(attrtype_lower_map[attrtype_lower],[]) old_value = filter(lambda x:x!=None,old_value) del attrtype_lower_map[attrtype_lower] else: old_value = [] if not old_value and new_value: # Add a new attribute to entry modlist.append((ldap.MOD_ADD,attrtype,new_value)) elif old_value and new_value: # Replace existing attribute replace_attr_value = len(old_value)!=len(new_value) if not replace_attr_value: case_insensitive = case_ignore_attr_types.has_key(attrtype_lower) old_value_dict=list_dict(old_value,case_insensitive) new_value_dict=list_dict(new_value,case_insensitive) delete_values = [] for v in old_value: if not new_value_dict.has_key(v): replace_attr_value = 1 break add_values = [] if not replace_attr_value: for v in new_value: if not old_value_dict.has_key(v): replace_attr_value = 1 break if replace_attr_value: modlist.append((ldap.MOD_DELETE,attrtype,None)) modlist.append((ldap.MOD_ADD,attrtype,new_value)) elif old_value and not new_value: # Completely delete an existing attribute modlist.append((ldap.MOD_DELETE,attrtype,None)) if not ignore_oldexistent: # Remove all attributes of old_entry which are not present # in new_entry at all for a in attrtype_lower_map.keys(): if ignore_attr_types.has_key(a): # This attribute type is ignored continue attrtype = attrtype_lower_map[a] modlist.append((ldap.MOD_DELETE,attrtype,None)) return modlist # modifyModlist() python-ldap-2.4.10/Lib/ldap/.cvsignore0000644000076400001440000000002507465025376020263 0ustar michaelusers00000000000000*.pyc *.pyo *.bck *~ python-ldap-2.4.10/Lib/ldap/__init__.py0000644000076400001440000000425511764172710020375 0ustar michaelusers00000000000000""" ldap - base module See http://www.python-ldap.org/ for details. $Id: __init__.py,v 1.83 2012/06/07 18:40:59 stroeder Exp $ """ # This is also the overall release version number __version__ = '2.4.10' import sys if __debug__: # Tracing is only supported in debugging mode import traceback _trace_level = 0 _trace_file = sys.stderr _trace_stack_limit = None import _ldap from _ldap import * OPT_NAMES_DICT = {} for k,v in vars(_ldap).items(): if k.startswith('OPT_'): OPT_NAMES_DICT[v]=k class DummyLock: """Define dummy class with methods compatible to threading.Lock""" def __init__(self): pass def acquire(self): pass def release(self): pass try: # Check if Python installation was build with thread support import thread except ImportError: LDAPLockBaseClass = DummyLock else: import threading LDAPLockBaseClass = threading.Lock class LDAPLock: """ Mainly a wrapper class to log all locking events. Note that this cumbersome approach with _lock attribute was taken since threading.Lock is not suitable for sub-classing. """ _min_trace_level = 3 def __init__(self,lock_class=None,desc=''): """ lock_class Class compatible to threading.Lock desc Description shown in debug log messages """ self._desc = desc self._lock = (lock_class or LDAPLockBaseClass)() def acquire(self): if __debug__: global _trace_level if _trace_level>=self._min_trace_level: _trace_file.write('***%s %s.acquire()\n' % (self._desc,self.__class__.__name__)) return self._lock.acquire() def release(self): if __debug__: global _trace_level if _trace_level>=self._min_trace_level: _trace_file.write('***%s %s.release()\n' % (self._desc,self.__class__.__name__)) return self._lock.release() # Create module-wide lock for serializing all calls into underlying LDAP lib _ldap_module_lock = LDAPLock(desc='Module wide') from functions import open,initialize,init,get_option,set_option from ldap.dn import explode_dn,explode_rdn,str2dn,dn2str del str2dn del dn2str # More constants # For compability of 2.3 and 2.4 OpenLDAP API OPT_DIAGNOSTIC_MESSAGE = OPT_ERROR_STRING python-ldap-2.4.10/Lib/ldap/functions.py0000644000076400001440000000651411704305353020641 0ustar michaelusers00000000000000""" functions.py - wraps functions of module _ldap See http://www.python-ldap.org/ for details. \$Id: functions.py,v 1.28 2011/11/23 17:27:46 stroeder Exp $ Compability: - Tested with Python 2.0+ but should work with Python 1.5.x - functions should behave exactly the same like in _ldap Usage: Directly imported by ldap/__init__.py. The symbols of _ldap are overridden. Thread-lock: Basically calls into the LDAP lib are serialized by the module-wide lock _ldapmodule_lock. """ from ldap import __version__ __all__ = [ 'open','initialize','init', 'explode_dn','explode_rdn', 'get_option','set_option', ] import sys,pprint,_ldap,ldap from ldap import LDAPError from ldap.dn import explode_dn,explode_rdn from ldap.ldapobject import LDAPObject if __debug__: # Tracing is only supported in debugging mode import traceback def _ldap_function_call(lock,func,*args,**kwargs): """ Wrapper function which locks and logs calls to function lock Instance of threading.Lock or compatible func Function to call with arguments passed in via *args and **kwargs """ if lock: lock.acquire() if __debug__: if ldap._trace_level>=1: ldap._trace_file.write('*** %s.%s %s\n' % ( '_ldap',func.__name__, pprint.pformat((args,kwargs)) )) if ldap._trace_level>=9: traceback.print_stack(limit=ldap._trace_stack_limit,file=ldap._trace_file) try: try: result = func(*args,**kwargs) finally: if lock: lock.release() except LDAPError,e: if __debug__ and ldap._trace_level>=2: ldap._trace_file.write('=> LDAPError: %s\n' % (str(e))) raise if __debug__ and ldap._trace_level>=2: ldap._trace_file.write('=> result:\n%s\n' % (pprint.pformat(result))) return result def initialize(uri,trace_level=0,trace_file=sys.stdout,trace_stack_limit=None): """ Return LDAPObject instance by opening LDAP connection to LDAP host specified by LDAP URL Parameters: uri LDAP URL containing at least connection scheme and hostport, e.g. ldap://localhost:389 trace_level If non-zero a trace output of LDAP calls is generated. trace_file File object where to write the trace output to. Default is to use stdout. """ return LDAPObject(uri,trace_level,trace_file,trace_stack_limit) def open(host,port=389,trace_level=0,trace_file=sys.stdout,trace_stack_limit=None): """ Return LDAPObject instance by opening LDAP connection to specified LDAP host Parameters: host LDAP host and port, e.g. localhost port integer specifying the port number to use, e.g. 389 trace_level If non-zero a trace output of LDAP calls is generated. trace_file File object where to write the trace output to. Default is to use stdout. """ import warnings warnings.warn('ldap.open() is deprecated! Use ldap.initialize() instead.', DeprecationWarning,2) return initialize('ldap://%s:%d' % (host,port),trace_level,trace_file,trace_stack_limit) init = open def get_option(option): """ get_option(name) -> value Get the value of an LDAP global option. """ return _ldap_function_call(None,_ldap.get_option,option) def set_option(option,invalue): """ set_option(name, value) Set the value of an LDAP global option. """ return _ldap_function_call(None,_ldap.set_option,option,invalue) python-ldap-2.4.10/Lib/ldap/schema/0000755000076400001440000000000011764172736017526 5ustar michaelusers00000000000000python-ldap-2.4.10/Lib/ldap/schema/models.py0000644000076400001440000005315111764172557021371 0ustar michaelusers00000000000000""" schema.py - support for subSchemaSubEntry information See http://www.python-ldap.org/ for details. \$Id: models.py,v 1.45 2012/03/07 19:07:15 stroeder Exp $ """ import UserDict,ldap.cidict from ldap.schema.tokenizer import split_tokens,extract_tokens if __debug__: from types import TupleType,StringType,IntType try: from types import BooleanType except ImportError: BooleanType = IntType NOT_HUMAN_READABLE_LDAP_SYNTAXES = { '1.3.6.1.4.1.1466.115.121.1.4':None, # Audio '1.3.6.1.4.1.1466.115.121.1.5':None, # Binary '1.3.6.1.4.1.1466.115.121.1.8':None, # Certificate '1.3.6.1.4.1.1466.115.121.1.9':None, # Certificate List '1.3.6.1.4.1.1466.115.121.1.10':None, # Certificate Pair '1.3.6.1.4.1.1466.115.121.1.23':None, # G3 FAX '1.3.6.1.4.1.1466.115.121.1.28':None, # JPEG '1.3.6.1.4.1.1466.115.121.1.40':None, # Octet String '1.3.6.1.4.1.1466.115.121.1.49':None, # Supported Algorithm } class SchemaElement: """ Base class for all schema element classes. Not used directly! Arguments: schema_element_str String which contains the schema element description to be parsed. Class attributes: schema_attribute LDAP attribute type containing a certain schema element description token_defaults Dictionary internally used by the schema element parser containing the defaults for certain schema description key-words """ token_defaults = { 'DESC':(None,), } def __init__(self,schema_element_str=None): if schema_element_str: l = split_tokens(schema_element_str,self.token_defaults) self.set_id(l[1]) d = extract_tokens(l,self.token_defaults) self._set_attrs(l,d) def _set_attrs(self,l,d): self.desc = d['DESC'][0] return def set_id(self,element_id): self.oid = element_id def get_id(self): return self.oid def key_attr(self,key,value,quoted=0): assert value is None or type(value)==StringType,TypeError("value has to be of StringType, was %s" % repr(value)) if value: if quoted: return " %s '%s'" % (key,value.replace("'","\\'")) else: return " %s %s" % (key,value) else: return "" def key_list(self,key,values,sep=' ',quoted=0): assert type(values)==TupleType,TypeError("values has to be of ListType") if not values: return '' if quoted: quoted_values = [ "'%s'" % value.replace("'","\\'") for value in values ] else: quoted_values = values if len(values)==1: return ' %s %s' % (key,quoted_values[0]) else: return ' %s ( %s )' % (key,sep.join(quoted_values)) def __str__(self): result = [str(self.oid)] result.append(self.key_attr('DESC',self.desc,quoted=1)) return '( %s )' % ''.join(result) class ObjectClass(SchemaElement): """ Arguments: schema_element_str String containing an ObjectClassDescription Class attributes: oid OID assigned to the object class names This list of strings contains all NAMEs of the object class desc This string contains description text (DESC) of the object class obsolete Integer flag (0 or 1) indicating whether the object class is marked as OBSOLETE in the schema must This list of strings contains NAMEs or OIDs of all attributes an entry of the object class must have may This list of strings contains NAMEs or OIDs of additional attributes an entry of the object class may have kind Kind of an object class: 0 = ABSTRACT, 1 = STRUCTURAL, 2 = AUXILIARY sup This list of strings contains NAMEs or OIDs of object classes this object class is derived from """ schema_attribute = 'objectClasses' token_defaults = { 'NAME':(()), 'DESC':(None,), 'OBSOLETE':None, 'SUP':(()), 'STRUCTURAL':None, 'AUXILIARY':None, 'ABSTRACT':None, 'MUST':(()), 'MAY':() } def _set_attrs(self,l,d): self.obsolete = d['OBSOLETE']!=None self.names = d['NAME'] self.desc = d['DESC'][0] self.must = d['MUST'] self.may = d['MAY'] # Default is STRUCTURAL, see RFC2552 or draft-ietf-ldapbis-syntaxes self.kind = 0 if d['ABSTRACT']!=None: self.kind = 1 elif d['AUXILIARY']!=None: self.kind = 2 if self.kind==0 and not d['SUP'] and self.oid!='2.5.6.0': # STRUCTURAL object classes are sub-classes of 'top' by default self.sup = ('top',) else: self.sup = d['SUP'] assert type(self.names)==TupleType assert self.desc is None or type(self.desc)==StringType assert type(self.obsolete)==BooleanType and (self.obsolete==0 or self.obsolete==1) assert type(self.sup)==TupleType assert type(self.kind)==IntType assert type(self.must)==TupleType assert type(self.may)==TupleType return def __str__(self): result = [str(self.oid)] result.append(self.key_list('NAME',self.names,quoted=1)) result.append(self.key_attr('DESC',self.desc,quoted=1)) result.append(self.key_list('SUP',self.sup,sep=' $ ')) result.append({0:'',1:' OBSOLETE'}[self.obsolete]) result.append({0:' STRUCTURAL',1:' ABSTRACT',2:' AUXILIARY'}[self.kind]) result.append(self.key_list('MUST',self.must,sep=' $ ')) result.append(self.key_list('MAY',self.may,sep=' $ ')) return '( %s )' % ''.join(result) AttributeUsage = ldap.cidict.cidict({ 'userApplication':0, # work-around for non-compliant schema 'userApplications':0, 'directoryOperation':1, 'distributedOperation':2, 'dSAOperation':3, }) class AttributeType(SchemaElement): """ Arguments: schema_element_str String containing an AttributeTypeDescription Class attributes: oid OID assigned to the attribute type names This list of strings contains all NAMEs of the attribute type desc This string contains description text (DESC) of the attribute type obsolete Integer flag (0 or 1) indicating whether the attribute type is marked as OBSOLETE in the schema single_value Integer flag (0 or 1) indicating whether the attribute must have only one value syntax String contains OID of the LDAP syntax assigned to the attribute type no_user_mod Integer flag (0 or 1) indicating whether the attribute is modifiable by a client application equality String contains NAME or OID of the matching rule used for checking whether attribute values are equal substr String contains NAME or OID of the matching rule used for checking whether an attribute value contains another value ordering String contains NAME or OID of the matching rule used for checking whether attribute values are lesser-equal than usage USAGE of an attribute type: 0 = userApplications 1 = directoryOperation, 2 = distributedOperation, 3 = dSAOperation sup This list of strings contains NAMEs or OIDs of attribute types this attribute type is derived from """ schema_attribute = 'attributeTypes' token_defaults = { 'NAME':(()), 'DESC':(None,), 'OBSOLETE':None, 'SUP':(()), 'EQUALITY':(None,), 'ORDERING':(None,), 'SUBSTR':(None,), 'SYNTAX':(None,), 'SINGLE-VALUE':None, 'COLLECTIVE':None, 'NO-USER-MODIFICATION':None, 'USAGE':('userApplications',) } def _set_attrs(self,l,d): self.names = d['NAME'] self.desc = d['DESC'][0] self.obsolete = d['OBSOLETE']!=None self.sup = d['SUP'] self.equality = d['EQUALITY'][0] self.ordering = d['ORDERING'][0] self.substr = d['SUBSTR'][0] try: syntax = d['SYNTAX'][0] except IndexError: self.syntax = None self.syntax_len = None else: if syntax is None: self.syntax = None self.syntax_len = None else: try: self.syntax,syntax_len = d['SYNTAX'][0].split("{") except ValueError: self.syntax = d['SYNTAX'][0] self.syntax_len = None for i in l: if i.startswith("{") and i.endswith("}"): self.syntax_len=long(i[1:-1]) else: self.syntax_len = long(syntax_len[:-1]) self.single_value = d['SINGLE-VALUE']!=None self.collective = d['COLLECTIVE']!=None self.no_user_mod = d['NO-USER-MODIFICATION']!=None self.usage = AttributeUsage.get(d['USAGE'][0],0) assert type(self.names)==TupleType assert self.desc is None or type(self.desc)==StringType assert type(self.sup)==TupleType,'attribute sup has type %s' % (type(self.sup)) assert type(self.obsolete)==BooleanType and (self.obsolete==0 or self.obsolete==1) assert type(self.single_value)==BooleanType and (self.single_value==0 or self.single_value==1) assert type(self.no_user_mod)==BooleanType and (self.no_user_mod==0 or self.no_user_mod==1) assert self.syntax is None or type(self.syntax)==StringType assert self.syntax_len is None or type(self.syntax_len)==type(0L) return def __str__(self): result = [str(self.oid)] result.append(self.key_list('NAME',self.names,quoted=1)) result.append(self.key_attr('DESC',self.desc,quoted=1)) result.append(self.key_list('SUP',self.sup,sep=' $ ')) result.append({0:'',1:' OBSOLETE'}[self.obsolete]) result.append(self.key_attr('EQUALITY',self.equality)) result.append(self.key_attr('ORDERING',self.ordering)) result.append(self.key_attr('SUBSTR',self.substr)) result.append(self.key_attr('SYNTAX',self.syntax)) if self.syntax_len!=None: result.append(('{%d}' % (self.syntax_len))*(self.syntax_len>0)) result.append({0:'',1:' SINGLE-VALUE'}[self.single_value]) result.append({0:'',1:' COLLECTIVE'}[self.collective]) result.append({0:'',1:' NO-USER-MODIFICATION'}[self.no_user_mod]) result.append( { 0:"", 1:" USAGE directoryOperation", 2:" USAGE distributedOperation", 3:" USAGE dSAOperation", }[self.usage] ) return '( %s )' % ''.join(result) class LDAPSyntax(SchemaElement): """ SyntaxDescription oid OID assigned to the LDAP syntax desc This string contains description text (DESC) of the LDAP syntax not_human_readable Integer flag (0 or 1) indicating whether the attribute type is marked as not human-readable (X-NOT-HUMAN-READABLE) """ schema_attribute = 'ldapSyntaxes' token_defaults = { 'DESC':(None,), 'X-NOT-HUMAN-READABLE':(None,), } def _set_attrs(self,l,d): self.desc = d['DESC'][0] self.not_human_readable = \ NOT_HUMAN_READABLE_LDAP_SYNTAXES.has_key(self.oid) or \ d['X-NOT-HUMAN-READABLE'][0]=='TRUE' assert self.desc is None or type(self.desc)==StringType return def __str__(self): result = [str(self.oid)] result.append(self.key_attr('DESC',self.desc,quoted=1)) result.append( {0:'',1:" X-NOT-HUMAN-READABLE 'TRUE'"}[self.not_human_readable] ) return '( %s )' % ''.join(result) class MatchingRule(SchemaElement): """ Arguments: schema_element_str String containing an MatchingRuleDescription Class attributes: oid OID assigned to the matching rule names This list of strings contains all NAMEs of the matching rule desc This string contains description text (DESC) of the matching rule obsolete Integer flag (0 or 1) indicating whether the matching rule is marked as OBSOLETE in the schema syntax String contains OID of the LDAP syntax this matching rule is usable with """ schema_attribute = 'matchingRules' token_defaults = { 'NAME':(()), 'DESC':(None,), 'OBSOLETE':None, 'SYNTAX':(None,), } def _set_attrs(self,l,d): self.names = d['NAME'] self.desc = d['DESC'][0] self.obsolete = d['OBSOLETE']!=None self.syntax = d['SYNTAX'][0] assert type(self.names)==TupleType assert self.desc is None or type(self.desc)==StringType assert type(self.obsolete)==BooleanType and (self.obsolete==0 or self.obsolete==1) assert self.syntax is None or type(self.syntax)==StringType return def __str__(self): result = [str(self.oid)] result.append(self.key_list('NAME',self.names,quoted=1)) result.append(self.key_attr('DESC',self.desc,quoted=1)) result.append({0:'',1:' OBSOLETE'}[self.obsolete]) result.append(self.key_attr('SYNTAX',self.syntax)) return '( %s )' % ''.join(result) class MatchingRuleUse(SchemaElement): """ Arguments: schema_element_str String containing an MatchingRuleUseDescription Class attributes: oid OID of the accompanying matching rule names This list of strings contains all NAMEs of the matching rule desc This string contains description text (DESC) of the matching rule obsolete Integer flag (0 or 1) indicating whether the matching rule is marked as OBSOLETE in the schema applies This list of strings contains NAMEs or OIDs of attribute types for which this matching rule is used """ schema_attribute = 'matchingRuleUse' token_defaults = { 'NAME':(()), 'DESC':(None,), 'OBSOLETE':None, 'APPLIES':(()), } def _set_attrs(self,l,d): self.names = d['NAME'] self.desc = d['DESC'][0] self.obsolete = d['OBSOLETE']!=None self.applies = d['APPLIES'] assert type(self.names)==TupleType assert self.desc is None or type(self.desc)==StringType assert type(self.obsolete)==BooleanType and (self.obsolete==0 or self.obsolete==1) assert type(self.applies)==TupleType return def __str__(self): result = [str(self.oid)] result.append(self.key_list('NAME',self.names,quoted=1)) result.append(self.key_attr('DESC',self.desc,quoted=1)) result.append({0:'',1:' OBSOLETE'}[self.obsolete]) result.append(self.key_list('APPLIES',self.applies,sep=' $ ')) return '( %s )' % ''.join(result) class DITContentRule(SchemaElement): """ Arguments: schema_element_str String containing an DITContentRuleDescription Class attributes: oid OID of the accompanying structural object class names This list of strings contains all NAMEs of the DIT content rule desc This string contains description text (DESC) of the DIT content rule obsolete Integer flag (0 or 1) indicating whether the DIT content rule is marked as OBSOLETE in the schema aux This list of strings contains NAMEs or OIDs of all auxiliary object classes usable in an entry of the object class must This list of strings contains NAMEs or OIDs of all attributes an entry of the object class must have which may extend the list of required attributes of the object classes of an entry may This list of strings contains NAMEs or OIDs of additional attributes an entry of the object class may have which may extend the list of optional attributes of the object classes of an entry nots This list of strings contains NAMEs or OIDs of attributes which may not be present in an entry of the object class """ schema_attribute = 'dITContentRules' token_defaults = { 'NAME':(()), 'DESC':(None,), 'OBSOLETE':None, 'AUX':(()), 'MUST':(()), 'MAY':(()), 'NOT':(()), } def _set_attrs(self,l,d): self.names = d['NAME'] self.desc = d['DESC'][0] self.obsolete = d['OBSOLETE']!=None self.aux = d['AUX'] self.must = d['MUST'] self.may = d['MAY'] self.nots = d['NOT'] assert type(self.names)==TupleType assert self.desc is None or type(self.desc)==StringType assert type(self.obsolete)==BooleanType and (self.obsolete==0 or self.obsolete==1) assert type(self.aux)==TupleType assert type(self.must)==TupleType assert type(self.may)==TupleType assert type(self.nots)==TupleType return def __str__(self): result = [str(self.oid)] result.append(self.key_list('NAME',self.names,quoted=1)) result.append(self.key_attr('DESC',self.desc,quoted=1)) result.append({0:'',1:' OBSOLETE'}[self.obsolete]) result.append(self.key_list('AUX',self.aux,sep=' $ ')) result.append(self.key_list('MUST',self.must,sep=' $ ')) result.append(self.key_list('MAY',self.may,sep=' $ ')) result.append(self.key_list('NOT',self.nots,sep=' $ ')) return '( %s )' % ''.join(result) class DITStructureRule(SchemaElement): """ Arguments: schema_element_str String containing an DITStructureRuleDescription Class attributes: ruleid rule ID of the DIT structure rule (only locally unique) names This list of strings contains all NAMEs of the DIT structure rule desc This string contains description text (DESC) of the DIT structure rule obsolete Integer flag (0 or 1) indicating whether the DIT content rule is marked as OBSOLETE in the schema form List of strings with NAMEs or OIDs of associated name forms sup List of strings with NAMEs or OIDs of allowed structural object classes of superior entries in the DIT """ schema_attribute = 'dITStructureRules' token_defaults = { 'NAME':(()), 'DESC':(None,), 'OBSOLETE':None, 'FORM':(None,), 'SUP':(()), } def set_id(self,element_id): self.ruleid = element_id def get_id(self): return self.ruleid def _set_attrs(self,l,d): self.names = d['NAME'] self.desc = d['DESC'][0] self.obsolete = d['OBSOLETE']!=None self.form = d['FORM'][0] self.sup = d['SUP'] assert type(self.names)==TupleType assert self.desc is None or type(self.desc)==StringType assert type(self.obsolete)==BooleanType and (self.obsolete==0 or self.obsolete==1) assert type(self.form)==StringType assert type(self.sup)==TupleType return def __str__(self): result = [str(self.ruleid)] result.append(self.key_list('NAME',self.names,quoted=1)) result.append(self.key_attr('DESC',self.desc,quoted=1)) result.append({0:'',1:' OBSOLETE'}[self.obsolete]) result.append(self.key_attr('FORM',self.form,quoted=0)) result.append(self.key_list('SUP',self.sup,sep=' $ ')) return '( %s )' % ''.join(result) class NameForm(SchemaElement): """ Arguments: schema_element_str String containing an NameFormDescription Class attributes: oid OID of the name form names This list of strings contains all NAMEs of the name form desc This string contains description text (DESC) of the name form obsolete Integer flag (0 or 1) indicating whether the name form is marked as OBSOLETE in the schema form List of strings with NAMEs or OIDs of associated name forms oc String with NAME or OID of structural object classes this name form is usable with must This list of strings contains NAMEs or OIDs of all attributes an RDN must contain may This list of strings contains NAMEs or OIDs of additional attributes an RDN may contain """ schema_attribute = 'nameForms' token_defaults = { 'NAME':(()), 'DESC':(None,), 'OBSOLETE':None, 'OC':(None,), 'MUST':(()), 'MAY':(()), } def _set_attrs(self,l,d): self.names = d['NAME'] self.desc = d['DESC'][0] self.obsolete = d['OBSOLETE']!=None self.oc = d['OC'][0] self.must = d['MUST'] self.may = d['MAY'] assert type(self.names)==TupleType assert self.desc is None or type(self.desc)==StringType assert type(self.obsolete)==BooleanType and (self.obsolete==0 or self.obsolete==1) assert type(self.oc)==StringType assert type(self.must)==TupleType assert type(self.may)==TupleType return def __str__(self): result = [str(self.oid)] result.append(self.key_list('NAME',self.names,quoted=1)) result.append(self.key_attr('DESC',self.desc,quoted=1)) result.append({0:'',1:' OBSOLETE'}[self.obsolete]) result.append(self.key_attr('OC',self.oc)) result.append(self.key_list('MUST',self.must,sep=' $ ')) result.append(self.key_list('MAY',self.may,sep=' $ ')) return '( %s )' % ''.join(result) class Entry(UserDict.UserDict): """ Schema-aware implementation of an LDAP entry class. Mainly it holds the attributes in a string-keyed dictionary with the OID as key. """ def __init__(self,schema,dn,entry): self._keytuple2attrtype = {} self._attrtype2keytuple = {} self._s = schema self.dn = dn UserDict.UserDict.__init__(self,{}) self.update(entry) def _at2key(self,nameoroid): """ Return tuple of OID and all sub-types of attribute type specified in nameoroid. """ try: # Mapping already in cache return self._attrtype2keytuple[nameoroid] except KeyError: # Mapping has to be constructed oid = self._s.getoid(ldap.schema.AttributeType,nameoroid) l = nameoroid.lower().split(';') l[0] = oid t = tuple(l) self._attrtype2keytuple[nameoroid] = t return t def update(self,dict): for key in dict.keys(): self[key] = dict[key] def __contains__(self,key): return self.has_key(key) def __getitem__(self,nameoroid): return self.data[self._at2key(nameoroid)] def __setitem__(self,nameoroid,attr_values): k = self._at2key(nameoroid) self._keytuple2attrtype[k] = nameoroid self.data[k] = attr_values def __delitem__(self,nameoroid): k = self._at2key(nameoroid) del self.data[k] del self._attrtype2keytuple[nameoroid] del self._keytuple2attrtype[k] def has_key(self,nameoroid): k = self._at2key(nameoroid) return self.data.has_key(k) def get(self,nameoroid,failobj): try: return self[nameoroid] except KeyError: return failobj def keys(self): return self._keytuple2attrtype.values() def items(self): return [ (k,self[k]) for k in self.keys() ] def attribute_types( self,attr_type_filter=None,raise_keyerror=1 ): """ Convenience wrapper around SubSchema.attribute_types() which passes object classes of this particular entry as argument to SubSchema.attribute_types() """ return self._s.attribute_types( self.get('objectClass',[]),attr_type_filter,raise_keyerror ) python-ldap-2.4.10/Lib/ldap/schema/.cvsignore0000644000076400001440000000002507535750774021530 0ustar michaelusers00000000000000*.pyc *.pyo *.bck *~ python-ldap-2.4.10/Lib/ldap/schema/__init__.py0000644000076400001440000000050211233034606021614 0ustar michaelusers00000000000000""" ldap.schema - LDAPv3 schema handling See http://www.python-ldap.org/ for details. \$Id: __init__.py,v 1.7 2009/07/26 11:09:58 stroeder Exp $ """ from ldap import __version__ from ldap.schema.subentry import SubSchema,SCHEMA_ATTRS,SCHEMA_CLASS_MAPPING,SCHEMA_ATTR_MAPPING,urlfetch from ldap.schema.models import * python-ldap-2.4.10/Lib/ldap/schema/tokenizer.py0000644000076400001440000000375111176114343022103 0ustar michaelusers00000000000000""" ldap.schema.tokenizer - Low-level parsing functions for schema element strings See http://www.python-ldap.org/ for details. \$Id: tokenizer.py,v 1.13 2009/04/29 18:13:55 stroeder Exp $ """ def split_tokens(s,keywordDict): """ Returns list of syntax elements with quotes and spaces stripped. """ result = [] result_append = result.append s_len = len(s) i = 0 while istart: result_append(s[start:i]) result_append(s[i]) i +=1 # Consume parentheses start = i elif s[i]==" " or s[i]=="$": if i>start: result_append(s[start:i]) i +=1 # Consume more space chars while istart: result_append(s[start:i]) i +=1 if i>=s_len: break start = i while i=start: result_append(s[start:i]) i +=1 return result # split_tokens() def extract_tokens(l,known_tokens): """ Returns dictionary of known tokens with all values """ assert l[0].strip()=="(" and l[-1].strip()==")",ValueError(l) result = {} result_has_key = result.has_key result.update(known_tokens) i = 0 l_len = len(l) while i=2: raise OIDNotUnique(attr_value) # Store the schema element instance in the central registry self.sed[se_class][se_id] = se_instance if hasattr(se_instance,'names'): for name in ldap.cidict.cidict({}.fromkeys(se_instance.names)).keys(): if check_uniqueness and name in self.name2oid[se_class]: self.non_unique_names[se_class][se_id] = None raise NameNotUnique(attr_value) else: self.name2oid[se_class][name] = se_id # Turn dict into list maybe more handy for applications self.non_unique_oids = self.non_unique_oids.keys() return # subSchema.__init__() def ldap_entry(self): """ Returns a dictionary containing the sub schema sub entry """ # Initialize the dictionary with empty lists entry = {} # Collect the schema elements and store them in # entry's attributes for se_class in self.sed.keys(): for se in self.sed[se_class].values(): se_str = str(se) try: entry[SCHEMA_ATTR_MAPPING[se_class]].append(se_str) except KeyError: entry[SCHEMA_ATTR_MAPPING[se_class]] = [ se_str ] return entry def listall(self,schema_element_class,schema_element_filters=None): """ Returns a list of OIDs of all available schema elements of a given schema element class. """ avail_se = self.sed[schema_element_class] if schema_element_filters: result = [] for se_key in avail_se.keys(): se = avail_se[se_key] for fk,fv in schema_element_filters: try: if getattr(se,fk) in fv: result.append(se_key) except AttributeError: pass else: result = avail_se.keys() return result def tree(self,schema_element_class,schema_element_filters=None): """ Returns a ldap.cidict.cidict dictionary representing the tree structure of the schema elements. """ assert schema_element_class in [ObjectClass,AttributeType] avail_se = self.listall(schema_element_class,schema_element_filters) top_node = '_' tree = ldap.cidict.cidict({top_node:[]}) # 1. Pass: Register all nodes for se in avail_se: tree[se] = [] # 2. Pass: Register all sup references for se_oid in avail_se: se_obj = self.get_obj(schema_element_class,se_oid,None) if se_obj.__class__!=schema_element_class: # Ignore schema elements not matching schema_element_class. # This helps with falsely assigned OIDs. continue assert se_obj.__class__==schema_element_class, \ "Schema element referenced by %s must be of class %s but was %s" % ( se_oid,schema_element_class.__name__,se_obj.__class__ ) for s in se_obj.sup or ('_',): sup_oid = self.getoid(schema_element_class,s) try: tree[sup_oid].append(se_oid) except: pass return tree def getoid(self,se_class,nameoroid,raise_keyerror=0): """ Get an OID by name or OID """ nameoroid_stripped = nameoroid.split(';')[0].strip() if nameoroid_stripped in self.sed[se_class]: # name_or_oid is already a registered OID return nameoroid_stripped else: try: result_oid = self.name2oid[se_class][nameoroid_stripped] except KeyError: if raise_keyerror: raise KeyError('No registered %s-OID for nameoroid %s' % (se_class.__name__,repr(nameoroid_stripped))) else: result_oid = nameoroid_stripped return result_oid def get_inheritedattr(self,se_class,nameoroid,name): """ Get a possibly inherited attribute specified by name of a schema element specified by nameoroid. Returns None if class attribute is not set at all. Raises KeyError if no schema element is found by nameoroid. """ se = self.sed[se_class][self.getoid(se_class,nameoroid)] try: result = getattr(se,name) except AttributeError: result = None if result is None and se.sup: result = self.get_inheritedattr(se_class,se.sup[0],name) return result def get_obj(self,se_class,nameoroid,default=None,raise_keyerror=0): """ Get a schema element by name or OID """ se_oid = self.getoid(se_class,nameoroid) try: se_obj = self.sed[se_class][se_oid] except KeyError: if raise_keyerror: raise KeyError('No ldap.schema.%s instance with nameoroid %s and se_oid %s' % ( se_class.__name__,repr(nameoroid),repr(se_oid)) ) else: se_obj = default return se_obj def get_inheritedobj(self,se_class,nameoroid,inherited=None): """ Get a schema element by name or OID with all class attributes set including inherited class attributes """ import copy inherited = inherited or [] se = copy.copy(self.sed[se_class].get(self.getoid(se_class,nameoroid))) if se and hasattr(se,'sup'): for class_attr_name in inherited: setattr(se,class_attr_name,self.get_inheritedattr(se_class,nameoroid,class_attr_name)) return se def get_syntax(self,nameoroid): """ Get the syntax of an attribute type specified by name or OID """ at_oid = self.getoid(AttributeType,nameoroid) try: at_obj = self.get_inheritedobj(AttributeType,at_oid) except KeyError: return None else: return at_obj.syntax def get_structural_oc(self,oc_list): """ Returns OID of structural object class in oc_list if any is present. Returns None else. """ # Get tree of all STRUCTURAL object classes oc_tree = self.tree(ObjectClass,[('kind',[0])]) # Filter all STRUCTURAL object classes struct_ocs = {} for oc_nameoroid in oc_list: oc_se = self.get_obj(ObjectClass,oc_nameoroid,None) if oc_se and oc_se.kind==0: struct_ocs[oc_se.oid] = None result = None struct_oc_list = struct_ocs.keys() while struct_oc_list: oid = struct_oc_list.pop() for child_oid in oc_tree[oid]: if struct_ocs.has_key(self.getoid(ObjectClass,child_oid)): break else: result = oid return result def get_applicable_aux_classes(self,nameoroid): """ Return a list of the applicable AUXILIARY object classes for a STRUCTURAL object class specified by 'nameoroid' if the object class is governed by a DIT content rule. If there's no DIT content rule all available AUXILIARY object classes are returned. """ content_rule = self.get_obj(DITContentRule,nameoroid) if content_rule: # Return AUXILIARY object classes from DITContentRule instance return content_rule.aux else: # list all AUXILIARY object classes return self.listall(ObjectClass,[('kind',[2])]) def attribute_types( self,object_class_list,attr_type_filter=None,raise_keyerror=1,ignore_dit_content_rule=0 ): """ Returns a 2-tuple of all must and may attributes including all inherited attributes of superior object classes by walking up classes along the SUP attribute. The attributes are stored in a ldap.cidict.cidict dictionary. object_class_list list of strings specifying object class names or OIDs attr_type_filter list of 2-tuples containing lists of class attributes which has to be matched raise_keyerror All KeyError exceptions for non-existent schema elements are ignored ignore_dit_content_rule A DIT content rule governing the structural object class is ignored """ AttributeType = ldap.schema.AttributeType ObjectClass = ldap.schema.ObjectClass # Map object_class_list to object_class_oids (list of OIDs) object_class_oids = [ self.getoid(ObjectClass,o) for o in object_class_list ] # Initialize oid_cache = {} r_must,r_may = ldap.cidict.cidict(),ldap.cidict.cidict() if '1.3.6.1.4.1.1466.101.120.111' in object_class_oids: # Object class 'extensibleObject' MAY carry every attribute type for at_obj in self.sed[AttributeType].values(): r_may[at_obj.oid] = at_obj # Loop over OIDs of all given object classes while object_class_oids: object_class_oid = object_class_oids.pop(0) # Check whether the objectClass with this OID # has already been processed if oid_cache.has_key(object_class_oid): continue # Cache this OID as already being processed oid_cache[object_class_oid] = None try: object_class = self.sed[ObjectClass][object_class_oid] except KeyError: if raise_keyerror: raise # Ignore this object class continue assert isinstance(object_class,ObjectClass) assert hasattr(object_class,'must'),ValueError(object_class_oid) assert hasattr(object_class,'may'),ValueError(object_class_oid) for a in object_class.must: se_oid = self.getoid(AttributeType,a,raise_keyerror=raise_keyerror) r_must[se_oid] = self.get_obj(AttributeType,se_oid,raise_keyerror=raise_keyerror) for a in object_class.may: se_oid = self.getoid(AttributeType,a,raise_keyerror=raise_keyerror) r_may[se_oid] = self.get_obj(AttributeType,se_oid,raise_keyerror=raise_keyerror) object_class_oids.extend([ self.getoid(ObjectClass,o) for o in object_class.sup ]) # Process DIT content rules if not ignore_dit_content_rule: structural_oc = self.get_structural_oc(object_class_list) if structural_oc: # Process applicable DIT content rule try: dit_content_rule = self.get_obj(DITContentRule,structural_oc,raise_keyerror=1) except KeyError: # Not DIT content rule found for structural objectclass pass else: for a in dit_content_rule.must: se_oid = self.getoid(AttributeType,a,raise_keyerror=raise_keyerror) r_must[se_oid] = self.get_obj(AttributeType,se_oid,raise_keyerror=raise_keyerror) for a in dit_content_rule.may: se_oid = self.getoid(AttributeType,a,raise_keyerror=raise_keyerror) r_may[se_oid] = self.get_obj(AttributeType,se_oid,raise_keyerror=raise_keyerror) for a in dit_content_rule.nots: a_oid = self.getoid(AttributeType,a,raise_keyerror=raise_keyerror) try: del r_may[a_oid] except KeyError: pass # Remove all mandantory attribute types from # optional attribute type list for a in r_may.keys(): if r_must.has_key(a): del r_may[a] # Apply attr_type_filter to results if attr_type_filter: for l in [r_must,r_may]: for a in l.keys(): for afk,afv in attr_type_filter: try: schema_attr_type = self.sed[AttributeType][a] except KeyError: if raise_keyerror: raise KeyError,'No attribute type found in sub schema by name %s' % (a) # If there's no schema element for this attribute type # but still KeyError is to be ignored we filter it away del l[a] break else: if not getattr(schema_attr_type,afk) in afv: del l[a] break return r_must,r_may # attribute_types() def urlfetch(uri,trace_level=0): """ Fetches a parsed schema entry by uri. If uri is a LDAP URL the LDAP server is queried directly. Otherwise uri is assumed to point to a LDIF file which is loaded with urllib. """ uri = uri.strip() if uri.startswith('ldap:') or uri.startswith('ldaps:') or uri.startswith('ldapi:'): import ldapurl ldap_url = ldapurl.LDAPUrl(uri) l=ldap.initialize(ldap_url.initializeUrl(),trace_level) l.protocol_version = ldap.VERSION3 l.simple_bind_s(ldap_url.who or '', ldap_url.cred or '') subschemasubentry_dn = l.search_subschemasubentry_s(ldap_url.dn) if subschemasubentry_dn is None: subschemasubentry_entry = None else: if ldap_url.attrs is None: schema_attrs = SCHEMA_ATTRS else: schema_attrs = ldap_url.attrs subschemasubentry_entry = l.read_subschemasubentry_s( subschemasubentry_dn,attrs=schema_attrs ) l.unbind_s() del l else: import urllib,ldif ldif_file = urllib.urlopen(uri) ldif_parser = ldif.LDIFRecordList(ldif_file,max_entries=1) ldif_parser.parse() subschemasubentry_dn,subschemasubentry_entry = ldif_parser.all_records[0] if subschemasubentry_dn!=None: parsed_sub_schema = ldap.schema.SubSchema(subschemasubentry_entry) else: parsed_sub_schema = None return subschemasubentry_dn, parsed_sub_schema python-ldap-2.4.10/Lib/ldap/cidict.py0000644000076400001440000000522111172111572020057 0ustar michaelusers00000000000000""" This is a convenience wrapper for dictionaries returned from LDAP servers containing attribute names of variable case. See http://www.python-ldap.org/ for details. $Id: cidict.py,v 1.13 2009/04/17 14:34:34 stroeder Exp $ """ __version__ = """$Revision: 1.13 $""" from UserDict import UserDict from string import lower class cidict(UserDict): """ Case-insensitive but case-respecting dictionary. """ def __init__(self,default=None): self._keys = {} UserDict.__init__(self,{}) self.update(default or {}) def __getitem__(self,key): return self.data[lower(key)] def __setitem__(self,key,value): lower_key = lower(key) self._keys[lower_key] = key self.data[lower_key] = value def __delitem__(self,key): lower_key = lower(key) del self._keys[lower_key] del self.data[lower_key] def update(self,dict): for key in dict.keys(): self[key] = dict[key] def has_key(self,key): return UserDict.has_key(self,lower(key)) def __contains__(self,key): return self.has_key(key) def get(self,key,failobj=None): try: return self[key] except KeyError: return failobj def keys(self): return self._keys.values() def items(self): result = [] for k in self._keys.values(): result.append((k,self[k])) return result def strlist_minus(a,b): """ Return list of all items in a which are not in b (a - b). a,b are supposed to be lists of case-insensitive strings. """ temp = cidict() for elt in b: temp[elt] = elt result = [ elt for elt in a if not temp.has_key(elt) ] return result def strlist_intersection(a,b): """ Return intersection of two lists of case-insensitive strings a,b. """ temp = cidict() for elt in a: temp[elt] = elt result = [ temp[elt] for elt in b if temp.has_key(elt) ] return result def strlist_union(a,b): """ Return union of two lists of case-insensitive strings a,b. """ temp = cidict() for elt in a: temp[elt] = elt for elt in b: temp[elt] = elt return temp.values() if __debug__ and __name__ == '__main__': x = { 'AbCDeF' : 123 } cix = cidict(x) assert cix["ABCDEF"] == 123 assert cix.get("ABCDEF",None) == 123 assert cix.get("not existent",None) is None cix["xYZ"] = 987 assert cix["XyZ"] == 987 assert cix.get("XyZ",None) == 987 cix_keys = cix.keys() cix_keys.sort() assert cix_keys==['AbCDeF','xYZ'],ValueError(repr(cix_keys)) cix_items = cix.items() cix_items.sort() assert cix_items==[('AbCDeF',123),('xYZ',987)],ValueError(repr(cix_items)) del cix["abcdEF"] assert not cix._keys.has_key("abcdef") assert not cix.has_key("AbCDef") python-ldap-2.4.10/Lib/ldap/ldapobject.py0000644000076400001440000010524611764172557020760 0ustar michaelusers00000000000000""" ldapobject.py - wraps class _ldap.LDAPObject See http://www.python-ldap.org/ for details. \$Id: ldapobject.py,v 1.133 2012/06/02 10:23:15 stroeder Exp $ Compability: - Tested with Python 2.0+ but should work with Python 1.5.x - LDAPObject class should be exactly the same like _ldap.LDAPObject Usage: Directly imported by ldap/__init__.py. The symbols of _ldap are overridden. Thread-lock: Basically calls into the LDAP lib are serialized by the module-wide lock self._ldap_object_lock. """ from ldap import __version__ __all__ = [ 'LDAPObject', 'SimpleLDAPObject', 'NonblockingLDAPObject', 'ReconnectLDAPObject', ] if __debug__: # Tracing is only supported in debugging mode import traceback import sys,time,pprint,_ldap,ldap,ldap.functions from ldap.schema import SCHEMA_ATTRS from ldap.controls import LDAPControl,DecodeControlTuples,RequestControlTuples from ldap.extop import ExtendedRequest,ExtendedResponse from ldap import LDAPError class SimpleLDAPObject: """ Drop-in wrapper class around _ldap.LDAPObject """ CLASSATTR_OPTION_MAPPING = { "protocol_version": ldap.OPT_PROTOCOL_VERSION, "deref": ldap.OPT_DEREF, "referrals": ldap.OPT_REFERRALS, "timelimit": ldap.OPT_TIMELIMIT, "sizelimit": ldap.OPT_SIZELIMIT, "network_timeout": ldap.OPT_NETWORK_TIMEOUT, "error_number":ldap.OPT_ERROR_NUMBER, "error_string":ldap.OPT_ERROR_STRING, "matched_dn":ldap.OPT_MATCHED_DN, } def __init__( self,uri, trace_level=0,trace_file=None,trace_stack_limit=5 ): self._trace_level = trace_level self._trace_file = trace_file or sys.stdout self._trace_stack_limit = trace_stack_limit self._uri = uri self._ldap_object_lock = self._ldap_lock() self._l = ldap.functions._ldap_function_call(ldap._ldap_module_lock,_ldap.initialize,uri) self.timeout = -1 self.protocol_version = ldap.VERSION3 def _ldap_lock(self): if ldap.LIBLDAP_R: return ldap.LDAPLock(desc=self._uri) else: return ldap._ldap_module_lock def _ldap_call(self,func,*args,**kwargs): """ Wrapper method mainly for serializing calls into OpenLDAP libs and trace logs """ self._ldap_object_lock.acquire() if __debug__: if self._trace_level>=1: self._trace_file.write('*** %s %s - %s\n%s\n' % ( repr(self), self._uri, '.'.join((self.__class__.__name__,func.__name__)), pprint.pformat((args,kwargs)) )) if self._trace_level>=9: traceback.print_stack(limit=self._trace_stack_limit,file=self._trace_file) diagnostic_message_success = None try: try: result = func(*args,**kwargs) if __debug__ and self._trace_level>=2: if func.__name__!="unbind_ext": diagnostic_message_success = self._l.get_option(ldap.OPT_DIAGNOSTIC_MESSAGE) finally: self._ldap_object_lock.release() except LDAPError,e: if __debug__ and self._trace_level>=2: self._trace_file.write('=> LDAPError - %s: %s\n' % (e.__class__.__name__,str(e))) raise else: if __debug__ and self._trace_level>=2: if not diagnostic_message_success is None: self._trace_file.write('=> diagnosticMessage: %s\n' % (repr(diagnostic_message_success))) self._trace_file.write('=> result:\n%s\n' % (pprint.pformat(result))) return result def __setattr__(self,name,value): if self.CLASSATTR_OPTION_MAPPING.has_key(name): self.set_option(self.CLASSATTR_OPTION_MAPPING[name],value) else: self.__dict__[name] = value def __getattr__(self,name): if self.CLASSATTR_OPTION_MAPPING.has_key(name): return self.get_option(self.CLASSATTR_OPTION_MAPPING[name]) elif self.__dict__.has_key(name): return self.__dict__[name] else: raise AttributeError,'%s has no attribute %s' % ( self.__class__.__name__,repr(name) ) def abandon_ext(self,msgid,serverctrls=None,clientctrls=None): """ abandon_ext(msgid[,serverctrls=None[,clientctrls=None]]) -> None abandon(msgid) -> None Abandons or cancels an LDAP operation in progress. The msgid should be the message id of an outstanding LDAP operation as returned by the asynchronous methods search(), modify() etc. The caller can expect that the result of an abandoned operation will not be returned from a future call to result(). """ return self._ldap_call(self._l.abandon_ext,msgid,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def abandon(self,msgid): return self.abandon_ext(msgid,None,None) def cancel(self,cancelid,serverctrls=None,clientctrls=None): """ cancel(cancelid[,serverctrls=None[,clientctrls=None]]) -> int Send cancels extended operation for an LDAP operation specified by cancelid. The cancelid should be the message id of an outstanding LDAP operation as returned by the asynchronous methods search(), modify() etc. The caller can expect that the result of an abandoned operation will not be returned from a future call to result(). In opposite to abandon() this extended operation gets an result from the server and thus should be preferred if the server supports it. """ return self._ldap_call(self._l.cancel,cancelid,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def cancel_s(self,cancelid,serverctrls=None,clientctrls=None): msgid = self.cancel(cancelid,serverctrls,clientctrls) try: res = self.result(msgid,all=1,timeout=self.timeout) except (ldap.CANCELLED,ldap.SUCCESS): res = None return res def add_ext(self,dn,modlist,serverctrls=None,clientctrls=None): """ add_ext(dn, modlist[,serverctrls=None[,clientctrls=None]]) -> int This function adds a new entry with a distinguished name specified by dn which means it must not already exist. The parameter modlist is similar to the one passed to modify(), except that no operation integer need be included in the tuples. """ return self._ldap_call(self._l.add_ext,dn,modlist,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def add_ext_s(self,dn,modlist,serverctrls=None,clientctrls=None): msgid = self.add_ext(dn,modlist,serverctrls,clientctrls) resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout) return resp_type, resp_data, resp_msgid, resp_ctrls def add(self,dn,modlist): """ add(dn, modlist) -> int This function adds a new entry with a distinguished name specified by dn which means it must not already exist. The parameter modlist is similar to the one passed to modify(), except that no operation integer need be included in the tuples. """ return self.add_ext(dn,modlist,None,None) def add_s(self,dn,modlist): msgid = self.add(dn,modlist) return self.result(msgid,all=1,timeout=self.timeout) def simple_bind(self,who='',cred='',serverctrls=None,clientctrls=None): """ simple_bind([who='' [,cred='']]) -> int """ return self._ldap_call(self._l.simple_bind,who,cred,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def simple_bind_s(self,who='',cred='',serverctrls=None,clientctrls=None): """ simple_bind_s([who='' [,cred='']]) -> 4-tuple """ msgid = self.simple_bind(who,cred,serverctrls,clientctrls) resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout) return resp_type, resp_data, resp_msgid, resp_ctrls def bind(self,who,cred,method=ldap.AUTH_SIMPLE): """ bind(who, cred, method) -> int """ assert method==ldap.AUTH_SIMPLE,'Only simple bind supported in LDAPObject.bind()' return self.simple_bind(who,cred) def bind_s(self,who,cred,method=ldap.AUTH_SIMPLE): """ bind_s(who, cred, method) -> None """ msgid = self.bind(who,cred,method) return self.result(msgid,all=1,timeout=self.timeout) def sasl_interactive_bind_s(self,who,auth,serverctrls=None,clientctrls=None,sasl_flags=ldap.SASL_QUIET): """ sasl_interactive_bind_s(who, auth [,serverctrls=None[,clientctrls=None[,sasl_flags=ldap.SASL_QUIET]]]) -> None """ return self._ldap_call(self._l.sasl_interactive_bind_s,who,auth,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls),sasl_flags) def compare_ext(self,dn,attr,value,serverctrls=None,clientctrls=None): """ compare_ext(dn, attr, value [,serverctrls=None[,clientctrls=None]]) -> int compare_ext_s(dn, attr, value [,serverctrls=None[,clientctrls=None]]) -> int compare(dn, attr, value) -> int compare_s(dn, attr, value) -> int Perform an LDAP comparison between the attribute named attr of entry dn, and the value value. The synchronous form returns 0 for false, or 1 for true. The asynchronous form returns the message id of the initiates request, and the result of the asynchronous compare can be obtained using result(). Note that this latter technique yields the answer by raising the exception objects COMPARE_TRUE or COMPARE_FALSE. A design bug in the library prevents value from containing nul characters. """ return self._ldap_call(self._l.compare_ext,dn,attr,value,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def compare_ext_s(self,dn,attr,value,serverctrls=None,clientctrls=None): msgid = self.compare_ext(dn,attr,value,serverctrls,clientctrls) try: resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout) except ldap.COMPARE_TRUE: return 1 except ldap.COMPARE_FALSE: return 0 return None def compare(self,dn,attr,value): return self.compare_ext(dn,attr,value,None,None) def compare_s(self,dn,attr,value): return self.compare_ext_s(dn,attr,value,None,None) def delete_ext(self,dn,serverctrls=None,clientctrls=None): """ delete(dn) -> int delete_s(dn) -> None delete_ext(dn[,serverctrls=None[,clientctrls=None]]) -> int delete_ext_s(dn[,serverctrls=None[,clientctrls=None]]) -> None Performs an LDAP delete operation on dn. The asynchronous form returns the message id of the initiated request, and the result can be obtained from a subsequent call to result(). """ return self._ldap_call(self._l.delete_ext,dn,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def delete_ext_s(self,dn,serverctrls=None,clientctrls=None): msgid = self.delete_ext(dn,serverctrls,clientctrls) resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout) return resp_type, resp_data, resp_msgid, resp_ctrls def delete(self,dn): return self.delete_ext(dn,None,None) def delete_s(self,dn): return self.delete_ext_s(dn,None,None) def extop(self,extreq,serverctrls=None,clientctrls=None): """ extop(extreq[,serverctrls=None[,clientctrls=None]]]) -> int extop_s(extreq[,serverctrls=None[,clientctrls=None[,extop_resp_class=None]]]]) -> (respoid,respvalue) Performs an LDAP extended operation. The asynchronous form returns the message id of the initiated request, and the result can be obtained from a subsequent call to extop_result(). The extreq is an instance of class ldap.extop.ExtendedRequest. If argument extop_resp_class is set to a sub-class of ldap.extop.ExtendedResponse this class is used to return an object of this class instead of a raw BER value in respvalue. """ return self._ldap_call(self._l.extop,extreq.requestName,extreq.encodedRequestValue(),RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def extop_result(self,msgid=ldap.RES_ANY,all=1,timeout=None): resulttype,msg,msgid,respctrls,respoid,respvalue = self.result4(msgid,all=1,timeout=self.timeout,add_ctrls=1,add_intermediates=1,add_extop=1) return (respoid,respvalue) def extop_s(self,extreq,serverctrls=None,clientctrls=None,extop_resp_class=None): msgid = self.extop(extreq,serverctrls,clientctrls) res = self.extop_result(msgid,all=1,timeout=self.timeout) if extop_resp_class: respoid,respvalue = res if extop_resp_class.responseName!=respoid: raise ldap.PROTOCOL_ERROR("Wrong OID in extended response! Expected %s, got %s" % (extop_resp_class.responseName,respoid)) return extop_resp_class(extop_resp_class.responseName,respvalue) else: return res def modify_ext(self,dn,modlist,serverctrls=None,clientctrls=None): """ modify_ext(dn, modlist[,serverctrls=None[,clientctrls=None]]) -> int """ return self._ldap_call(self._l.modify_ext,dn,modlist,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def modify_ext_s(self,dn,modlist,serverctrls=None,clientctrls=None): msgid = self.modify_ext(dn,modlist,serverctrls,clientctrls) resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout) return resp_type, resp_data, resp_msgid, resp_ctrls def modify(self,dn,modlist): """ modify(dn, modlist) -> int modify_s(dn, modlist) -> None modify_ext(dn, modlist[,serverctrls=None[,clientctrls=None]]) -> int modify_ext_s(dn, modlist[,serverctrls=None[,clientctrls=None]]) -> None Performs an LDAP modify operation on an entry's attributes. dn is the DN of the entry to modify, and modlist is the list of modifications to make to the entry. Each element of the list modlist should be a tuple of the form (mod_op,mod_type,mod_vals), where mod_op is the operation (one of MOD_ADD, MOD_DELETE, MOD_INCREMENT or MOD_REPLACE), mod_type is a string indicating the attribute type name, and mod_vals is either a string value or a list of string values to add, delete, increment by or replace respectively. For the delete operation, mod_vals may be None indicating that all attributes are to be deleted. The asynchronous modify() returns the message id of the initiated request. """ return self.modify_ext(dn,modlist,None,None) def modify_s(self,dn,modlist): msgid = self.modify(dn,modlist) return self.result(msgid,all=1,timeout=self.timeout) def modrdn(self,dn,newrdn,delold=1): """ modrdn(dn, newrdn [,delold=1]) -> int modrdn_s(dn, newrdn [,delold=1]) -> None Perform a modify RDN operation. These routines take dn, the DN of the entry whose RDN is to be changed, and newrdn, the new RDN to give to the entry. The optional parameter delold is used to specify whether the old RDN should be kept as an attribute of the entry or not. The asynchronous version returns the initiated message id. This operation is emulated by rename() and rename_s() methods since the modrdn2* routines in the C library are deprecated. """ return self.rename(dn,newrdn,None,delold) def modrdn_s(self,dn,newrdn,delold=1): return self.rename_s(dn,newrdn,None,delold) def passwd(self,user,oldpw,newpw,serverctrls=None,clientctrls=None): return self._ldap_call(self._l.passwd,user,oldpw,newpw,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def passwd_s(self,user,oldpw,newpw,serverctrls=None,clientctrls=None): msgid = self.passwd(user,oldpw,newpw,serverctrls,clientctrls) return self.extop_result(msgid,all=1,timeout=self.timeout) def rename(self,dn,newrdn,newsuperior=None,delold=1,serverctrls=None,clientctrls=None): """ rename(dn, newrdn [, newsuperior=None [,delold=1][,serverctrls=None[,clientctrls=None]]]) -> int rename_s(dn, newrdn [, newsuperior=None] [,delold=1][,serverctrls=None[,clientctrls=None]]) -> None Perform a rename entry operation. These routines take dn, the DN of the entry whose RDN is to be changed, newrdn, the new RDN, and newsuperior, the new parent DN, to give to the entry. If newsuperior is None then only the RDN is modified. The optional parameter delold is used to specify whether the old RDN should be kept as an attribute of the entry or not. The asynchronous version returns the initiated message id. This actually corresponds to the rename* routines in the LDAP-EXT C API library. """ return self._ldap_call(self._l.rename,dn,newrdn,newsuperior,delold,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def rename_s(self,dn,newrdn,newsuperior=None,delold=1,serverctrls=None,clientctrls=None): msgid = self.rename(dn,newrdn,newsuperior,delold,serverctrls,clientctrls) resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout) return resp_type, resp_data, resp_msgid, resp_ctrls def result(self,msgid=ldap.RES_ANY,all=1,timeout=None): """ result([msgid=RES_ANY [,all=1 [,timeout=None]]]) -> (result_type, result_data) This method is used to wait for and return the result of an operation previously initiated by one of the LDAP asynchronous operation routines (eg search(), modify(), etc.) They all returned an invocation identifier (a message id) upon successful initiation of their operation. This id is guaranteed to be unique across an LDAP session, and can be used to request the result of a specific operation via the msgid parameter of the result() method. If the result of a specific operation is required, msgid should be set to the invocation message id returned when the operation was initiated; otherwise RES_ANY should be supplied. The all parameter only has meaning for search() responses and is used to select whether a single entry of the search response should be returned, or to wait for all the results of the search before returning. A search response is made up of zero or more search entries followed by a search result. If all is 0, search entries will be returned one at a time as they come in, via separate calls to result(). If all is 1, the search response will be returned in its entirety, i.e. after all entries and the final search result have been received. For all set to 0, result tuples trickle in (with the same message id), and with the result type RES_SEARCH_ENTRY, until the final result which has a result type of RES_SEARCH_RESULT and a (usually) empty data field. When all is set to 1, only one result is returned, with a result type of RES_SEARCH_RESULT, and all the result tuples listed in the data field. The method returns a tuple of the form (result_type, result_data). The result_type is one of the constants RES_*. See search() for a description of the search result's result_data, otherwise the result_data is normally meaningless. The result() method will block for timeout seconds, or indefinitely if timeout is negative. A timeout of 0 will effect a poll. The timeout can be expressed as a floating-point value. If timeout is None the default in self.timeout is used. If a timeout occurs, a TIMEOUT exception is raised, unless polling (timeout = 0), in which case (None, None) is returned. """ resp_type, resp_data, resp_msgid = self.result2(msgid,all,timeout) return resp_type, resp_data def result2(self,msgid=ldap.RES_ANY,all=1,timeout=None): resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all,timeout) return resp_type, resp_data, resp_msgid def result3(self,msgid=ldap.RES_ANY,all=1,timeout=None,resp_ctrl_classes=None): resp_type, resp_data, resp_msgid, decoded_resp_ctrls, retoid, retval = self.result4( msgid,all,timeout, add_ctrls=0,add_intermediates=0,add_extop=0, resp_ctrl_classes=resp_ctrl_classes ) return resp_type, resp_data, resp_msgid, decoded_resp_ctrls def result4(self,msgid=ldap.RES_ANY,all=1,timeout=None,add_ctrls=0,add_intermediates=0,add_extop=0,resp_ctrl_classes=None): if timeout is None: timeout = self.timeout ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates,add_extop) if ldap_result is None: resp_type, resp_data, resp_msgid, resp_ctrls, resp_name, resp_value = (None,None,None,None,None,None) else: if len(ldap_result)==4: resp_type, resp_data, resp_msgid, resp_ctrls = ldap_result resp_name, resp_value = None,None else: resp_type, resp_data, resp_msgid, resp_ctrls, resp_name, resp_value = ldap_result if add_ctrls: resp_data = [ (t,r,DecodeControlTuples(c,resp_ctrl_classes)) for t,r,c in resp_data ] decoded_resp_ctrls = DecodeControlTuples(resp_ctrls,resp_ctrl_classes) return resp_type, resp_data, resp_msgid, decoded_resp_ctrls, resp_name, resp_value def search_ext(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0): """ search(base, scope [,filterstr='(objectClass=*)' [,attrlist=None [,attrsonly=0]]]) -> int search_s(base, scope [,filterstr='(objectClass=*)' [,attrlist=None [,attrsonly=0]]]) search_st(base, scope [,filterstr='(objectClass=*)' [,attrlist=None [,attrsonly=0 [,timeout=-1]]]]) search_ext(base,scope,[,filterstr='(objectClass=*)' [,attrlist=None [,attrsonly=0 [,serverctrls=None [,clientctrls=None [,timeout=-1 [,sizelimit=0]]]]]]]) search_ext_s(base,scope,[,filterstr='(objectClass=*)' [,attrlist=None [,attrsonly=0 [,serverctrls=None [,clientctrls=None [,timeout=-1 [,sizelimit=0]]]]]]]) Perform an LDAP search operation, with base as the DN of the entry at which to start the search, scope being one of SCOPE_BASE (to search the object itself), SCOPE_ONELEVEL (to search the object's immediate children), or SCOPE_SUBTREE (to search the object and all its descendants). filter is a string representation of the filter to apply in the search (see RFC 4515). Each result tuple is of the form (dn,entry), where dn is a string containing the DN (distinguished name) of the entry, and entry is a dictionary containing the attributes. Attributes types are used as string dictionary keys and attribute values are stored in a list as dictionary value. The DN in dn is extracted using the underlying ldap_get_dn(), which may raise an exception of the DN is malformed. If attrsonly is non-zero, the values of attrs will be meaningless (they are not transmitted in the result). The retrieved attributes can be limited with the attrlist parameter. If attrlist is None, all the attributes of each entry are returned. serverctrls=None clientctrls=None The synchronous form with timeout, search_st() or search_ext_s(), will block for at most timeout seconds (or indefinitely if timeout is negative). A TIMEOUT exception is raised if no result is received within the time. The amount of search results retrieved can be limited with the sizelimit parameter if non-zero. """ return self._ldap_call( self._l.search_ext, base,scope,filterstr, attrlist,attrsonly, RequestControlTuples(serverctrls), RequestControlTuples(clientctrls), timeout,sizelimit, ) def search_ext_s(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0): msgid = self.search_ext(base,scope,filterstr,attrlist,attrsonly,serverctrls,clientctrls,timeout,sizelimit) return self.result(msgid,all=1,timeout=timeout)[1] def search(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0): return self.search_ext(base,scope,filterstr,attrlist,attrsonly,None,None) def search_s(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0): return self.search_ext_s(base,scope,filterstr,attrlist,attrsonly,None,None,timeout=self.timeout) def search_st(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,timeout=-1): return self.search_ext_s(base,scope,filterstr,attrlist,attrsonly,None,None,timeout) def start_tls_s(self): """ start_tls_s() -> None Negotiate TLS with server. The `version' attribute must have been set to VERSION3 before calling start_tls_s. If TLS could not be started an exception will be raised. """ return self._ldap_call(self._l.start_tls_s) def unbind_ext(self,serverctrls=None,clientctrls=None): """ unbind() -> int unbind_s() -> None unbind_ext() -> int unbind_ext_s() -> None This call is used to unbind from the directory, terminate the current association, and free resources. Once called, the connection to the LDAP server is closed and the LDAP object is invalid. Further invocation of methods on the object will yield an exception. The unbind and unbind_s methods are identical, and are synchronous in nature """ return self._ldap_call(self._l.unbind_ext,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) def unbind_ext_s(self,serverctrls=None,clientctrls=None): msgid = self.unbind_ext(serverctrls,clientctrls) if msgid!=None: resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout) return resp_type, resp_data, resp_msgid, resp_ctrls def unbind(self): return self.unbind_ext(None,None) def unbind_s(self): return self.unbind_ext_s(None,None) def whoami_s(self,serverctrls=None,clientctrls=None): return self._ldap_call(self._l.whoami_s,serverctrls,clientctrls) def get_option(self,option): result = self._ldap_call(self._l.get_option,option) if option==ldap.OPT_SERVER_CONTROLS or option==ldap.OPT_CLIENT_CONTROLS: result = DecodeControlTuples(result) return result def set_option(self,option,invalue): if option==ldap.OPT_SERVER_CONTROLS or option==ldap.OPT_CLIENT_CONTROLS: invalue = RequestControlTuples(invalue) return self._ldap_call(self._l.set_option,option,invalue) def search_subschemasubentry_s(self,dn=''): """ Returns the distinguished name of the sub schema sub entry for a part of a DIT specified by dn. None as result indicates that the DN of the sub schema sub entry could not be determined. """ try: r = self.search_s( dn,ldap.SCOPE_BASE,'(objectClass=*)',['subschemaSubentry'] ) except (ldap.NO_SUCH_OBJECT,ldap.NO_SUCH_ATTRIBUTE,ldap.INSUFFICIENT_ACCESS): r = [] except ldap.UNDEFINED_TYPE: return None try: if r: e = ldap.cidict.cidict(r[0][1]) search_subschemasubentry_dn = e.get('subschemaSubentry',[None])[0] if search_subschemasubentry_dn is None: if dn: # Try to find sub schema sub entry in root DSE return self.search_subschemasubentry_s(dn='') else: # If dn was already root DSE we can return here return None else: return search_subschemasubentry_dn except IndexError: return None def read_subschemasubentry_s(self,subschemasubentry_dn,attrs=None): """ Returns the sub schema sub entry's data """ attrs = attrs or SCHEMA_ATTRS try: r = self.search_s( subschemasubentry_dn,ldap.SCOPE_BASE, '(objectClass=subschema)', attrs ) except ldap.NO_SUCH_OBJECT: return None else: if r: return r[0][1] else: return None class NonblockingLDAPObject(SimpleLDAPObject): def __init__(self,uri,trace_level=0,trace_file=None,result_timeout=-1): self._result_timeout = result_timeout SimpleLDAPObject.__init__(self,uri,trace_level,trace_file) def result(self,msgid=ldap.RES_ANY,all=1,timeout=-1): """ """ ldap_result = self._ldap_call(self._l.result,msgid,0,self._result_timeout) if not all: return ldap_result start_time = time.time() all_results = [] while all: while ldap_result[0] is None: if (timeout>=0) and (time.time()-start_time>timeout): self._ldap_call(self._l.abandon,msgid) raise ldap.TIMEOUT( "LDAP time limit (%d secs) exceeded." % (timeout) ) time.sleep(0.00001) ldap_result = self._ldap_call(self._l.result,msgid,0,self._result_timeout) if ldap_result[1] is None: break all_results.extend(ldap_result[1]) ldap_result = None,None return all_results def search_st(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,timeout=-1): msgid = self.search(base,scope,filterstr,attrlist,attrsonly) return self.result(msgid,all=1,timeout=timeout) class ReconnectLDAPObject(SimpleLDAPObject): """ In case of server failure (ldap.SERVER_DOWN) the implementations of all synchronous operation methods (search_s() etc.) are doing an automatic reconnect and rebind and will retry the very same operation. This is very handy for broken LDAP server implementations (e.g. in Lotus Domino) which drop connections very often making it impossible to have a long-lasting control flow in the application. """ __transient_attrs__ = { '_l':None, '_ldap_object_lock':None, '_trace_file':None, } def __init__( self,uri, trace_level=0,trace_file=None,trace_stack_limit=5, retry_max=1,retry_delay=60.0 ): """ Parameters like SimpleLDAPObject.__init__() with these additional arguments: retry_max Maximum count of reconnect trials retry_delay Time span to wait between two reconnect trials """ self._uri = uri self._options = [] self._last_bind = None self._pending_reconnect = 0 SimpleLDAPObject.__init__(self,uri,trace_level,trace_file,trace_stack_limit) self._retry_max = retry_max self._retry_delay = retry_delay self._start_tls = 0 self._reconnects_done = 0L def __getstate__(self): """return data representation for pickled object""" d = {} for k,v in self.__dict__.items(): if not self.__transient_attrs__.has_key(k): d[k] = v return d def __setstate__(self,d): """set up the object from pickled data""" self.__dict__.update(d) self._ldap_object_lock = self._ldap_lock() self._trace_file = sys.stdout self.reconnect(self._uri) def _apply_last_bind(self): if self._last_bind!=None: func,args,kwargs = self._last_bind func(*args,**kwargs) def _restore_options(self): """Restore all recorded options""" for k,v in self._options: SimpleLDAPObject.set_option(self,k,v) def reconnect(self,uri): # Drop and clean up old connection completely # Reconnect while self._pending_reconnect: time.sleep(0.01) else: self._pending_reconnect = 1 reconnect_counter = self._retry_max while reconnect_counter: if __debug__ and self._trace_level>=1: self._trace_file.write('*** Try %d. reconnect to %s...\n' % ( self._retry_max-reconnect_counter+1,uri )) try: # Do the connect self._l = ldap.functions._ldap_function_call(ldap._ldap_module_lock,_ldap.initialize,uri) self._restore_options() # StartTLS extended operation in case this was called before if self._start_tls: self.start_tls_s() # Repeat last simple or SASL bind self._apply_last_bind() except ldap.SERVER_DOWN,e: SimpleLDAPObject.unbind_s(self) del self._l if __debug__ and self._trace_level>=1: self._trace_file.write('*** %d. reconnect to %s failed\n' % ( self._retry_max-reconnect_counter+1,uri )) reconnect_counter = reconnect_counter-1 if not reconnect_counter: raise if __debug__ and self._trace_level>=1: self._trace_file.write('=> delay %s...\n' % (self._retry_delay)) time.sleep(self._retry_delay) else: if __debug__ and self._trace_level>=1: self._trace_file.write('*** %d. reconnect to %s successful, last operation will be repeated\n' % ( self._retry_max-reconnect_counter+1,uri )) self._reconnects_done = self._reconnects_done + 1L break self._pending_reconnect = 0 return # reconnect() def _apply_method_s(self,func,*args,**kwargs): if not self.__dict__.has_key('_l'): self.reconnect(self._uri) try: return func(self,*args,**kwargs) except ldap.SERVER_DOWN: SimpleLDAPObject.unbind_s(self) del self._l # Try to reconnect self.reconnect(self._uri) # Re-try last operation return func(self,*args,**kwargs) def set_option(self,option,invalue): self._options.append((option,invalue)) return SimpleLDAPObject.set_option(self,option,invalue) def bind_s(self,*args,**kwargs): res = self._apply_method_s(SimpleLDAPObject.bind_s,*args,**kwargs) self._last_bind = (self.bind_s,args,kwargs) return res def simple_bind_s(self,*args,**kwargs): res = self._apply_method_s(SimpleLDAPObject.simple_bind_s,*args,**kwargs) self._last_bind = (self.simple_bind_s,args,kwargs) return res def start_tls_s(self,*args,**kwargs): res = self._apply_method_s(SimpleLDAPObject.start_tls_s,*args,**kwargs) self._start_tls = 1 return res def sasl_interactive_bind_s(self,*args,**kwargs): """ sasl_interactive_bind_s(who, auth) -> None """ res = self._apply_method_s(SimpleLDAPObject.sasl_interactive_bind_s,*args,**kwargs) self._last_bind = (self.sasl_interactive_bind_s,args,kwargs) return res def add_ext_s(self,*args,**kwargs): return self._apply_method_s(SimpleLDAPObject.add_ext_s,*args,**kwargs) def cancel_s(self,*args,**kwargs): return self._apply_method_s(SimpleLDAPObject.cancel_s,*args,**kwargs) def compare_s(self,*args,**kwargs): return self._apply_method_s(SimpleLDAPObject.compare_s,*args,**kwargs) def delete_ext_s(self,*args,**kwargs): return self._apply_method_s(SimpleLDAPObject.delete_ext_s,*args,**kwargs) def extop_s(self,*args,**kwargs): return self._apply_method_s(SimpleLDAPObject.extop_s,*args,**kwargs) def modify_ext_s(self,*args,**kwargs): return self._apply_method_s(SimpleLDAPObject.modify_ext_s,*args,**kwargs) def rename_s(self,*args,**kwargs): return self._apply_method_s(SimpleLDAPObject.rename_s,*args,**kwargs) def search_ext_s(self,*args,**kwargs): return self._apply_method_s(SimpleLDAPObject.search_ext_s,*args,**kwargs) def whoami_s(self,*args,**kwargs): return self._apply_method_s(SimpleLDAPObject.whoami_s,*args,**kwargs) # The class called LDAPObject will be used as default for # ldap.open() and ldap.initialize() LDAPObject = SimpleLDAPObject python-ldap-2.4.10/Lib/ldap/resiter.py0000644000076400001440000000166111643040601020276 0ustar michaelusers00000000000000""" ldap.resiter - processing LDAP results with iterators See http://www.python-ldap.org/ for details. \$Id: resiter.py,v 1.6 2011/07/28 08:23:32 stroeder Exp $ Python compability note: Requires Python 2.3+ """ class ResultProcessor: """ Mix-in class used with ldap.ldapopbject.LDAPObject or derived classes. """ def allresults(self,msgid,timeout=-1): """ Generator function which returns an iterator for processing all LDAP operation results of the given msgid retrieved with LDAPObject.result3() -> 4-tuple """ result_type,result_list,result_msgid,result_serverctrls = self.result3(msgid,0,timeout) while result_type and result_list: # Loop over list of search results for result_item in result_list: yield (result_type,result_list,result_msgid,result_serverctrls) result_type,result_list,result_msgid,result_serverctrls = self.result3(msgid,0,timeout) return # allresults() python-ldap-2.4.10/Lib/ldap/async.py0000644000076400001440000002010511643040601017730 0ustar michaelusers00000000000000""" ldap.async - handle async LDAP operations See http://www.python-ldap.org/ for details. \$Id: async.py,v 1.32 2011/07/28 08:51:38 stroeder Exp $ Python compability note: Tested on Python 2.0+ but should run on Python 1.5.x. """ import ldap from ldap import __version__ _searchResultTypes={ ldap.RES_SEARCH_ENTRY:None, ldap.RES_SEARCH_RESULT:None, ldap.RES_SEARCH_REFERENCE:None, } _entryResultTypes={ ldap.RES_SEARCH_ENTRY:None, ldap.RES_SEARCH_RESULT:None, } class WrongResultType(Exception): def __init__(self,receivedResultType,expectedResultTypes): self.receivedResultType = receivedResultType self.expectedResultTypes = expectedResultTypes Exception.__init__(self) def __str__(self): return 'Received wrong result type %s (expected one of %s).' % ( self.receivedResultType, ', '.join(self.expectedResultTypes), ) class AsyncSearchHandler: """ Class for stream-processsing LDAP search results Arguments: l LDAPObject instance """ def __init__(self,l): self._l = l self._msgId = None def startSearch( self, searchRoot, searchScope, filterStr, attrList=None, attrsOnly=0, timeout=-1, sizelimit=0, serverctrls=None, clientctrls=None ): """ searchRoot See parameter base of method LDAPObject.search() searchScope See parameter scope of method LDAPObject.search() filterStr See parameter filter of method LDAPObject.search() attrList=None See parameter attrlist of method LDAPObject.search() attrsOnly See parameter attrsonly of method LDAPObject.search() timeout Maximum time the server shall use for search operation sizelimit Maximum number of entries a server should return (request client-side limit) serverctrls list of server-side LDAP controls clientctrls list of client-side LDAP controls """ self._msgId = self._l.search_ext( searchRoot,searchScope,filterStr, attrList,attrsOnly,serverctrls,clientctrls,timeout,sizelimit ) return # startSearch() def preProcessing(self): """ Do anything you want after starting search but before receiving and processing results """ def postProcessing(self): """ Do anything you want after receiving and processing results """ def processResults(self,ignoreResultsNumber=0,processResultsCount=0,timeout=-1): """ ignoreResultsNumber Don't process the first ignoreResultsNumber results. processResultsCount If non-zero this parameters indicates the number of results processed is limited to processResultsCount. timeout See parameter timeout of ldap.LDAPObject.result() """ self.preProcessing() result_counter = 0 end_result_counter = ignoreResultsNumber+processResultsCount go_ahead = 1 partial = 0 self.beginResultsDropped = 0 self.endResultBreak = result_counter try: result_type,result_list = None,None while go_ahead: while result_type is None and not result_list: result_type,result_list,result_msgid,result_serverctrls = self._l.result3(self._msgId,0,timeout) if not result_list: break if not _searchResultTypes.has_key(result_type): raise WrongResultType(result_type,_searchResultTypes.keys()) # Loop over list of search results for result_item in result_list: if result_counter 'z' or c in "\\*()": c = "\\%02x" % ord(c) r.append(c) elif escape_mode==2: for c in assertion_value: r.append("\\%02x" % ord(c)) else: raise ValueError('escape_mode must be 0, 1 or 2.') s = ''.join(r) else: s = assertion_value.replace('\\', r'\5c') s = s.replace(r'*', r'\2a') s = s.replace(r'(', r'\28') s = s.replace(r')', r'\29') s = s.replace('\x00', r'\00') return s def filter_format(filter_template,assertion_values): """ filter_template String containing %s as placeholder for assertion values. assertion_values List or tuple of assertion values. Length must match count of %s in filter_template. """ return filter_template % (tuple(map(escape_filter_chars,assertion_values))) python-ldap-2.4.10/Lib/ldap/syncrepl.py0000644000076400001440000004100011652061041020450 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ ldap.syncrepl - for implementing syncrepl consumer (see RFC 4533) See http://www.python-ldap.org/ for project details. $Id: syncrepl.py,v 1.2 2011/10/26 19:40:17 stroeder Exp $ """ #__all__ = [ # '', # '', #] from uuid import UUID # Imports from python-ldap 2.4+ import ldap.ldapobject from ldap.controls import RequestControl,ResponseControl,KNOWN_RESPONSE_CONTROLS # Imports from pyasn1 from pyasn1.type import tag,namedtype,namedval,univ,constraint from pyasn1.codec.ber import encoder,decoder __all__ = [ 'SyncreplConsumer' ] # RFC 4533: # # syncUUID ::= OCTET STRING (SIZE(16)) # syncCookie ::= OCTET STRING class syncUUID(univ.OctetString): subtypeSpec = constraint.ValueSizeConstraint(16,16) class syncCookie(univ.OctetString): pass # 2.2. Sync Request Control # # The Sync Request Control is an LDAP Control [RFC4511] where the # controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.1 and the # controlValue, an OCTET STRING, contains a BER-encoded # syncRequestValue. The criticality field is either TRUE or FALSE. # # syncRequestValue ::= SEQUENCE { # mode ENUMERATED { # -- 0 unused # refreshOnly (1), # -- 2 reserved # refreshAndPersist (3) # }, # cookie syncCookie OPTIONAL, # reloadHint BOOLEAN DEFAULT FALSE # } # # The Sync Request Control is only applicable to the SearchRequest # Message. class syncRequestMode(univ.Enumerated): namedValues = namedval.NamedValues( ('refreshOnly', 1), ('refreshAndPersist', 3) ) subtypeSpec = univ.Enumerated.subtypeSpec + constraint.SingleValueConstraint(1,3) class syncRequestValue(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType('mode', syncRequestMode()), namedtype.OptionalNamedType('cookie', syncCookie()), namedtype.DefaultedNamedType('reloadHint', univ.Boolean(False)) ) class SyncRequestControl(RequestControl): controlType = '1.3.6.1.4.1.4203.1.9.1.1' def __init__(self, criticality=1, cookie=None, mode='refreshOnly', reloadHint=False): self.criticality = criticality self.cookie = cookie self.mode = mode self.reloadHint = reloadHint def encodeControlValue(self): r = syncRequestValue() r.setComponentByName('mode', syncRequestMode(self.mode)) if self.cookie is not None: r.setComponentByName('cookie', syncCookie(self.cookie)) if self.reloadHint: r.setComponentbyName('reloadHint', univ.Boolean(self.reloadHint)) return encoder.encode(r) # 2.3. Sync State Control # # The Sync State Control is an LDAP Control [RFC4511] where the # controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.2 and the # controlValue, an OCTET STRING, contains a BER-encoded syncStateValue. # The criticality is FALSE. # # syncStateValue ::= SEQUENCE { # state ENUMERATED { # present (0), # add (1), # modify (2), # delete (3) # }, # entryUUID syncUUID, # cookie syncCookie OPTIONAL # } # # The Sync State Control is only applicable to SearchResultEntry and # SearchResultReference Messages. class syncStateOp(univ.Enumerated): namedValues = namedval.NamedValues( ('present', 0), ('add', 1), ('modify', 2), ('delete', 3) ) subtypeSpec = univ.Enumerated.subtypeSpec + constraint.SingleValueConstraint(0,1,2,3) class syncStateValue(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType('state', syncStateOp()), namedtype.NamedType('entryUUID', syncUUID()), namedtype.OptionalNamedType('cookie', syncCookie()) ) class SyncStateControl(ResponseControl): controlType = '1.3.6.1.4.1.4203.1.9.1.2' opnames = ( 'present', 'add', 'modify', 'delete' ) def decodeControlValue(self, encodedControlValue): d = decoder.decode(encodedControlValue, asn1Spec = syncStateValue()) state = d[0].getComponentByName('state') uuid = UUID(bytes=d[0].getComponentByName('entryUUID')) self.cookie = d[0].getComponentByName('cookie') self.state = self.__class__.opnames[int(state)] self.entryUUID = str(uuid) if self.cookie is not None: self.cookie = str(self.cookie) KNOWN_RESPONSE_CONTROLS[SyncStateControl.controlType] = SyncStateControl # 2.4. Sync Done Control # # The Sync Done Control is an LDAP Control [RFC4511] where the # controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.3 and the # controlValue contains a BER-encoded syncDoneValue. The criticality # is FALSE (and hence absent). # # syncDoneValue ::= SEQUENCE { # cookie syncCookie OPTIONAL, # refreshDeletes BOOLEAN DEFAULT FALSE # } # # The Sync Done Control is only applicable to the SearchResultDone # Message. class syncDoneValue(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.OptionalNamedType('cookie', syncCookie()), namedtype.DefaultedNamedType('refreshDeletes', univ.Boolean(False)) ) class SyncDoneControl(ResponseControl): controlType = '1.3.6.1.4.1.4203.1.9.1.3' def decodeControlValue(self, encodedControlValue): d = decoder.decode(encodedControlValue, asn1Spec = syncDoneValue()) self.cookie = d[0].getComponentByName('cookie') self.refreshDeletes = d[0].getComponentByName('refreshDeletes') if self.cookie is not None: self.cookie = str(self.cookie) if self.refreshDeletes is not None: self.refreshDeletes = bool(self.refreshDeletes) KNOWN_RESPONSE_CONTROLS[SyncDoneControl.controlType] = SyncDoneControl # 2.5. Sync Info Message # # The Sync Info Message is an LDAP Intermediate Response Message # [RFC4511] where responseName is the object identifier # 1.3.6.1.4.1.4203.1.9.1.4 and responseValue contains a BER-encoded # syncInfoValue. The criticality is FALSE (and hence absent). # # syncInfoValue ::= CHOICE { # newcookie [0] syncCookie, # refreshDelete [1] SEQUENCE { # cookie syncCookie OPTIONAL, # refreshDone BOOLEAN DEFAULT TRUE # }, # refreshPresent [2] SEQUENCE { # cookie syncCookie OPTIONAL, # refreshDone BOOLEAN DEFAULT TRUE # }, # syncIdSet [3] SEQUENCE { # cookie syncCookie OPTIONAL, # refreshDeletes BOOLEAN DEFAULT FALSE, # syncUUIDs SET OF syncUUID # } # } # class refreshDelete(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.OptionalNamedType('cookie', syncCookie()), namedtype.DefaultedNamedType('refreshDone', univ.Boolean(True)) ) class refreshPresent(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.OptionalNamedType('cookie', syncCookie()), namedtype.DefaultedNamedType('refreshDone', univ.Boolean(True)) ) class syncUUIDs(univ.SetOf): componentType = syncUUID() class syncIdSet(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.OptionalNamedType('cookie', syncCookie()), namedtype.DefaultedNamedType('refreshDeletes', univ.Boolean(False)), namedtype.NamedType('syncUUIDs', syncUUIDs()) ) class syncInfoValue(univ.Choice): componentType = namedtype.NamedTypes( namedtype.NamedType( 'newcookie', syncCookie().subtype( implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0) ) ), namedtype.NamedType( 'refreshDelete', refreshDelete().subtype( implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1) ) ), namedtype.NamedType( 'refreshPresent', refreshPresent().subtype( implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2) ) ), namedtype.NamedType( 'syncIdSet', syncIdSet().subtype( implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3) ) ) ) class SyncInfoMessage: responseName = '1.3.6.1.4.1.4203.1.9.1.4' def __init__(self, encodedMessage): d = decoder.decode(encodedMessage, asn1Spec = syncInfoValue()) self.newcookie = None self.refreshDelete = None self.refreshPresent = None self.syncIdSet = None for attr in [ 'newcookie', 'refreshDelete', 'refreshPresent', 'syncIdSet']: comp = d[0].getComponentByName(attr) if comp is not None: if attr == 'newcookie': self.newcookie = str(comp) return val = dict() cookie = comp.getComponentByName('cookie') if cookie is not None: val['cookie'] = str(cookie) if attr.startswith('refresh'): val['refreshDone'] = bool(comp.getComponentByName('refreshDone')) elif attr == 'syncIdSet': uuids = [] ids = comp.getComponentByName('syncUUIDs') for i in range(len(ids)): uuid = UUID(bytes=str(ids.getComponentByPosition(i))) uuids.append(str(uuid)) val['syncUUIDs'] = uuids val['refreshDeletes'] = bool(comp.getComponentByName('refreshDeletes')) setattr(self,attr,val) return class SyncreplConsumer: """ SyncreplConsumer - LDAP syncrepl consumer object. """ def syncrepl_search(self, base, scope, mode='refreshOnly', cookie=None, **search_args): """ Starts syncrepl search operation. base, scope, and **search_args are passed along to self.search_ext unmodified (aside from adding a Sync Request control to any serverctrls provided). mode provides syncrepl mode. Can be 'refreshOnly' to finish after synchronization, or 'refreshAndPersist' to persist (continue to receive updates) after synchronization. cookie: an opaque value representing the replication state of the client. Subclasses should override the syncrepl_set_cookie() and syncrepl_get_cookie() methods to store the cookie appropriately, rather than passing it. """ if cookie is None: cookie = self.syncrepl_get_cookie() syncreq = SyncRequestControl(cookie=cookie, mode=mode) if 'serverctrls' in search_args: search_args['serverctrls'] += [syncreq] else: search_args['serverctrls'] = [syncreq] self.__refreshDone = False return self.search_ext(base, scope, **search_args) def syncrepl_poll(self, msgid=-1, timeout=None, all=0): """ polls for and processes responses to the syncrepl_search() operation. Returns False when operation finishes, True if it is in progress, or raises an exception on error. If timeout is specified, raises ldap.TIMEOUT in the event of a timeout. If all is set to a nonzero value, poll() will return only when finished or when an exception is raised. """ while True: type, msg, mid, ctrls, n, v = self.result4( msgid=msgid, timeout=timeout, add_intermediates=1, add_ctrls=1, all = 0 ) if type == 101: # search result. This marks the end of a refreshOnly session. # look for a SyncDone control, save the cookie, and if necessary # delete non-present entries. for c in ctrls: if c.__class__.__name__ != 'SyncDoneControl': continue self.syncrepl_present(None,refreshDeletes=c.refreshDeletes) if c.cookie is not None: self.syncrepl_set_cookie(c.cookie) return False elif type == 100: # search entry with associated SyncState control for m in msg: dn, attrs, ctrls = m for c in ctrls: if c.__class__.__name__ != 'SyncStateControl': continue if c.state == 'present': self.syncrepl_present([c.entryUUID]) elif c.state == 'delete': self.syncrepl_delete([c.entryUUID]) else: self.syncrepl_entry(dn, attrs, c.entryUUID) if self.__refreshDone is False: self.syncrepl_present([c.entryUUID]) if c.cookie is not None: self.syncrepl_set_cookie(c.cookie) break elif type == 121: # Intermediate message. If it is a SyncInfoMessage, parse it for m in msg: rname, resp, ctrls = m if rname != SyncInfoMessage.responseName: continue sim = SyncInfoMessage(resp) if sim.newcookie is not None: self.syncrepl_set_cookie(sim.newcookie) elif sim.refreshPresent is not None: self.syncrepl_present(None, refreshDeletes=False) if 'cookie' in sim.refreshPresent: self.syncrepl_set_cookie(sim.refreshPresent['cookie']) self.__refreshDone=sim.refreshPresent['refreshDone'] elif sim.refreshDelete is not None: self.syncrepl_present(None, refreshDeletes=True) if 'cookie' in sim.refreshDelete: self.syncrepl_set_cookie(sim.refreshDelete['cookie']) self.__refreshDone=sim.refreshDelete['refreshDone'] elif sim.syncIdSet is not None: if sim.syncIdSet['refreshDeletes'] is True: self.syncrepl_delete(sim.syncIdSet['syncUUIDs']) else: self.syncrepl_present(sim.syncIdSet['syncUUIDs']) if 'cookie' in sim.syncIdSet: self.syncrepl_set_cookie(sim.syncIdSet['cookie']) pass if all == 0: return True # virtual methods -- subclass must override these to do useful work def syncrepl_set_cookie(self, cookie): """ Called by syncrepl_poll() to store a new cookie provided by the server. """ pass def syncrepl_get_cookie(self): """ Called by syncrepl_search() to retreive the cookie stored by syncrepl_set_cookie() """ pass def syncrepl_present(self, uuids, refreshDeletes=False): """ Called by syncrepl_poll() whenever entry UUIDs are presented to the client. syncrepl_present() is given a list of entry UUIDs (uuids) and a flag (refreshDeletes) which indicates whether the server explicitly deleted non-present entries during the refresh operation. If called with a list of uuids, the syncrepl_present() implementation should record those uuids as present in the directory. If called with uuids set to None and refreshDeletes set to False, syncrepl_present() should delete all non-present entries from the local mirror, and reset the list of recorded uuids. If called with uuids set to None and refreshDeletes set to True, syncrepl_present() should reset the list of recorded uuids, without deleting any entries. """ pass def syncrepl_delete(self, uuids): """ Called by syncrepl_poll() to delete entries. A list of UUIDs of the entries to be deleted is given in the uuids parameter. """ pass def syncrepl_entry(self, dn, attrs, uuid): """ Called by syncrepl_poll() for any added or modified entries. The provided uuid is used to identify the provided entry in any future modification (including dn modification), deletion, and presentation operations. """ pass python-ldap-2.4.10/Lib/ldap/sasl.py0000644000076400001440000001000411233034606017555 0ustar michaelusers00000000000000""" sasl.py - support for SASL mechanism See http://www.python-ldap.org/ for details. \$Id: sasl.py,v 1.15 2009/07/26 11:09:58 stroeder Exp $ Description: The ldap.sasl module provides SASL authentication classes. Each class provides support for one SASL mechanism. This is done by implementing a callback() - method, which will be called by the LDAPObject's sasl_bind_s() method Implementing support for new sasl mechanism is very easy --- see the examples of digest_md5 and gssapi. Compability: - Tested with Python 2.0+ but should work with Python 1.5.x """ from ldap import __version__ if __debug__: # Tracing is only supported in debugging mode import traceback from ldap import _trace_level,_trace_file,_trace_stack_limit # These are the SASL callback id's , as defined in sasl.h CB_USER = 0x4001 CB_AUTHNAME = 0x4002 CB_LANGUAGE = 0x4003 CB_PASS = 0x4004 CB_ECHOPROMPT = 0x4005 CB_NOECHOPROMPT= 0x4006 CB_GETREALM = 0x4007 class sasl: """This class handles SASL interactions for authentication. If an instance of this class is passed to ldap's sasl_bind_s() method, the library will call its callback() method. For specific SASL authentication mechanisms, this method can be overridden""" def __init__(self,cb_value_dict,mech): """ The (generic) base class takes a cb_value_dictionary of question-answer pairs. Questions are specified by the respective SASL callback id's. The mech argument is a string that specifies the SASL mechaninsm to be uesd.""" self.cb_value_dict = cb_value_dict or {} self.mech = mech def callback(self,cb_id,challenge,prompt,defresult): """ The callback method will be called by the sasl_bind_s() method several times. Each time it will provide the id, which tells us what kind of information is requested (the CB_ ... constants above). The challenge might be a short (english) text or some binary string, from which the return value is calculated. The prompt argument is always a human-readable description string; The defresult is a default value provided by the sasl library Currently, we do not use the challenge and prompt information, and return only information which is stored in the self.cb_value_dict cb_value_dictionary. Note that the current callback interface is not very useful for writing generic sasl GUIs, which would need to know all the questions to ask, before the answers are returned to the sasl lib (in contrast to one question at a time).""" # The following print command might be useful for debugging # new sasl mechanisms. So it is left here cb_result = self.cb_value_dict.get(cb_id,defresult) or '' if __debug__: if _trace_level>=1: _trace_file.write("*** id=%d, challenge=%s, prompt=%s, defresult=%s\n-> %s\n" % ( cb_id, challenge, prompt, repr(defresult), repr(self.cb_value_dict.get(cb_result)) )) return cb_result class cram_md5(sasl): """This class handles SASL CRAM-MD5 authentication.""" def __init__(self,authc_id, password, authz_id=""): auth_dict = {CB_AUTHNAME:authc_id, CB_PASS:password, CB_USER:authz_id} sasl.__init__(self,auth_dict,"CRAM-MD5") class digest_md5(sasl): """This class handles SASL DIGEST-MD5 authentication.""" def __init__(self,authc_id, password, authz_id=""): auth_dict = {CB_AUTHNAME:authc_id, CB_PASS:password, CB_USER:authz_id} sasl.__init__(self,auth_dict,"DIGEST-MD5") class gssapi(sasl): """This class handles SASL GSSAPI (i.e. Kerberos V) authentication.""" def __init__(self,authz_id=""): sasl.__init__(self, {CB_USER:authz_id},"GSSAPI") class external(sasl): """This class handles SASL EXTERNAL authentication (i.e. X.509 client certificate)""" def __init__(self,authz_id=""): sasl.__init__(self, {CB_USER:authz_id},"EXTERNAL") python-ldap-2.4.10/Lib/ldap/dn.py0000644000076400001440000000535111401717577017241 0ustar michaelusers00000000000000""" dn.py - misc stuff for handling distinguished names (see RFC 4514) See http://www.python-ldap.org/ for details. \$Id: dn.py,v 1.11 2010/06/03 12:26:39 stroeder Exp $ Compability: - Tested with Python 2.0+ """ from ldap import __version__ import _ldap import ldap.functions def escape_dn_chars(s): """ Escape all DN special characters found in s with a back-slash (see RFC 4514, section 2.4) """ if s: s = s.replace('\\','\\\\') s = s.replace(',' ,'\\,') s = s.replace('+' ,'\\+') s = s.replace('"' ,'\\"') s = s.replace('<' ,'\\<') s = s.replace('>' ,'\\>') s = s.replace(';' ,'\\;') s = s.replace('=' ,'\\=') s = s.replace('\000' ,'\\\000') if s[0]=='#' or s[0]==' ': s = ''.join(('\\',s)) if s[-1]==' ': s = ''.join((s[:-1],'\\ ')) return s def str2dn(dn,flags=0): """ This function takes a DN as string as parameter and returns a decomposed DN. It's the inverse to dn2str(). flags describes the format of the dn See also the OpenLDAP man-page ldap_str2dn(3) """ if not dn: return [] return ldap.functions._ldap_function_call(None,_ldap.str2dn,dn,flags) def dn2str(dn): """ This function takes a decomposed DN as parameter and returns a single string. It's the inverse to str2dn() but will always return a DN in LDAPv3 format compliant to RFC 4514. """ return ','.join([ '+'.join([ '='.join((atype,escape_dn_chars(avalue or ''))) for atype,avalue,dummy in rdn]) for rdn in dn ]) def explode_dn(dn,notypes=0,flags=0): """ explode_dn(dn [, notypes=0]) -> list This function takes a DN and breaks it up into its component parts. The notypes parameter is used to specify that only the component's attribute values be returned and not the attribute types. """ if not dn: return [] dn_decomp = str2dn(dn,flags) rdn_list = [] for rdn in dn_decomp: if notypes: rdn_list.append('+'.join([ escape_dn_chars(avalue or '') for atype,avalue,dummy in rdn ])) else: rdn_list.append('+'.join([ '='.join((atype,escape_dn_chars(avalue or ''))) for atype,avalue,dummy in rdn ])) return rdn_list def explode_rdn(rdn,notypes=0,flags=0): """ explode_rdn(rdn [, notypes=0]) -> list This function takes a RDN and breaks it up into its component parts if it is a multi-valued RDN. The notypes parameter is used to specify that only the component's attribute values be returned and not the attribute types. """ if not rdn: return [] rdn_decomp = str2dn(rdn,flags)[0] if notypes: return [avalue or '' for atype,avalue,dummy in rdn_decomp] else: return ['='.join((atype,escape_dn_chars(avalue or ''))) for atype,avalue,dummy in rdn_decomp] python-ldap-2.4.10/Lib/dsml.py0000644000076400001440000002007311764172710016651 0ustar michaelusers00000000000000""" dsml - generate and parse DSMLv1 data (see http://www.oasis-open.org/committees/dsml/) See http://www.python-ldap.org/ for details. $Id: dsml.py,v 1.27 2012/06/07 18:40:59 stroeder Exp $ Python compability note: Tested with Python 2.0+. """ __version__ = '2.4.10' import string,base64 special_entities = ( ('&','&'), ('<','<'), ('"','"'), ("'",'''), ) def replace_char(s): for char,entity in special_entities: s = string.replace(s,char,entity) return s class DSMLWriter: """ Class for writing LDAP entry records to a DSMLv1 file. Arguments: f File object for output. base64_attrs Attribute types to be base64-encoded. dsml_comment Text placed in comment lines behind . indent String used for indentiation of next nested level. """ def __init__( self,f,base64_attrs=[],dsml_comment='',indent=' ' ): self._output_file = f self._base64_attrs = {}.fromkeys(map(string.lower,base64_attrs)) self._dsml_comment = dsml_comment self._indent = indent def _needs_base64_encoding(self,attr_type,attr_value): if self._base64_attrs: return self._base64_attrs.has_key(string.lower(attr_type)) else: try: unicode(attr_value,'utf-8') except UnicodeError: return 1 else: return 0 def writeHeader(self): """ Write the header """ self._output_file.write('\n'.join([ '', '', '', '%s\n' % (self._indent), ]) ) if self._dsml_comment: self._output_file.write('%s\n' % (self._indent)) def writeFooter(self): """ Write the footer """ self._output_file.write('%s\n' % (self._indent)) self._output_file.write('\n') def unparse(self,dn,entry): return self.writeRecord(dn,entry) def writeRecord(self,dn,entry): """ dn string-representation of distinguished name entry dictionary holding the LDAP entry {attr:data} """ # Write line dn: first self._output_file.write( '%s\n' % ( self._indent*2,replace_char(dn) ) ) objectclasses = entry.get('objectclass',entry.get('objectClass',[])) self._output_file.write('%s\n' % (self._indent*3)) for oc in objectclasses: self._output_file.write('%s%s\n' % (self._indent*4,oc)) self._output_file.write('%s\n' % (self._indent*3)) attr_types = entry.keys()[:] try: attr_types.remove('objectclass') attr_types.remove('objectClass') except ValueError: pass attr_types.sort() for attr_type in attr_types: self._output_file.write('%s\n' % (self._indent*3,attr_type)) for attr_value_item in entry[attr_type]: needs_base64_encoding = self._needs_base64_encoding( attr_type,attr_value_item ) if needs_base64_encoding: attr_value_item = base64.encodestring(attr_value_item) else: attr_value_item = replace_char(attr_value_item) self._output_file.write('%s\n' % ( self._indent*4, ' encoding="base64"'*needs_base64_encoding ) ) self._output_file.write('%s%s\n' % ( self._indent*5, attr_value_item ) ) self._output_file.write('%s\n' % ( self._indent*4, ) ) self._output_file.write('%s\n' % (self._indent*3)) self._output_file.write('%s\n' % (self._indent*2)) return try: import xml.sax,xml.sax.handler except ImportError: pass else: class DSMLv1Handler(xml.sax.handler.ContentHandler): """ Content handler class for DSMLv1 """ def __init__(self,parser_instance): self._parser_instance = parser_instance xml.sax.handler.ContentHandler.__init__(self) def startDocument(self): pass def endDocument(self): pass def startElement(self,raw_name,attrs): assert raw_name.startswith(''),'Illegal name' name = raw_name[5:] if name=='dsml': pass elif name=='directory-entries': self._parsing_entries = 1 elif name=='entry': self._dn = attrs['dn'] self._entry = {} elif name=='attr': self._attr_type = attrs['name'].encode('utf-8') self._attr_values = [] elif name=='value': self._attr_value = '' self._base64_encoding = attrs.get('encoding','').lower()=='base64' # Handle object class tags elif name=='objectclass': self._object_classes = [] elif name=='oc-value': self._oc_value = '' # Unhandled tags else: raise ValueError,'Unknown tag %s' % (raw_name) def endElement(self,raw_name): assert raw_name.startswith('dsml:'),'Illegal name' name = raw_name[5:] if name=='dsml': pass elif name=='directory-entries': self._parsing_entries = 0 elif name=='entry': self._parser_instance.handle(self._dn,self._entry) del self._dn del self._entry elif name=='attr': self._entry[self._attr_type] = self._attr_values del self._attr_type del self._attr_values elif name=='value': if self._base64_encoding: attr_value = base64.decodestring(self._attr_value.strip()) else: attr_value = self._attr_value.strip().encode('utf-8') self._attr_values.append(attr_value) del attr_value del self._attr_value del self._base64_encoding # Handle object class tags elif name=='objectclass': self._entry['objectClass'] = self._object_classes del self._object_classes elif name=='oc-value': self._object_classes.append(self._oc_value.strip().encode('utf-8')) del self._oc_value # Unhandled tags else: raise ValueError,'Unknown tag %s' % (raw_name) def characters(self,ch): if self.__dict__.has_key('_oc_value'): self._oc_value = self._oc_value + ch elif self.__dict__.has_key('_attr_value'): self._attr_value = self._attr_value + ch else: pass class DSMLParser: """ Base class for a DSMLv1 parser. Applications should sub-class this class and override method handle() to implement something meaningful. Public class attributes: records_read Counter for records processed so far Arguments: input_file File-object to read the DSMLv1 input from ignored_attr_types Attributes with these attribute type names will be ignored. max_entries If non-zero specifies the maximum number of entries to be read from f. line_sep String used as line separator """ def __init__( self, input_file, ContentHandlerClass, ignored_attr_types=None, max_entries=0, ): self._input_file = input_file self._max_entries = max_entries self._ignored_attr_types = {}.fromkeys(map(string.lower,(ignored_attr_types or []))) self._current_record = None,None self.records_read = 0 self._parser = xml.sax.make_parser() self._parser.setFeature(xml.sax.handler.feature_namespaces,0) content_handler = ContentHandlerClass(self) self._parser.setContentHandler(content_handler) def handle(self,*args,**kwargs): """ Process a single DSMLv1 entry record. This method should be implemented by applications using DSMLParser. """ import pprint pprint.pprint(args) pprint.pprint(kwargs) def parse(self): """ Continously read and parse DSML records """ self._parser.parse(self._input_file) python-ldap-2.4.10/Lib/ldif.py0000644000076400001440000003264111764172710016634 0ustar michaelusers00000000000000""" ldif - generate and parse LDIF data (see RFC 2849) See http://www.python-ldap.org/ for details. $Id: ldif.py,v 1.67 2012/06/07 18:40:59 stroeder Exp $ Python compability note: Tested with Python 2.0+, but should work with Python 1.5.2+. """ __version__ = '2.4.10' __all__ = [ # constants 'ldif_pattern', # functions 'AttrTypeandValueLDIF','CreateLDIF','ParseLDIF', # classes 'LDIFWriter', 'LDIFParser', 'LDIFRecordList', 'LDIFCopy', ] import urlparse,urllib,base64,re,types try: from cStringIO import StringIO except ImportError: from StringIO import StringIO attrtype_pattern = r'[\w;.-]+(;[\w_-]+)*' attrvalue_pattern = r'(([^,]|\\,)+|".*?")' attrtypeandvalue_pattern = attrtype_pattern + r'[ ]*=[ ]*' + attrvalue_pattern rdn_pattern = attrtypeandvalue_pattern + r'([ ]*\+[ ]*' + attrtypeandvalue_pattern + r')*[ ]*' dn_pattern = rdn_pattern + r'([ ]*,[ ]*' + rdn_pattern + r')*[ ]*' dn_regex = re.compile('^%s$' % dn_pattern) ldif_pattern = '^((dn(:|::) %(dn_pattern)s)|(%(attrtype_pattern)s(:|::) .*)$)+' % vars() MOD_OP_INTEGER = { 'add':0,'delete':1,'replace':2 } MOD_OP_STR = { 0:'add',1:'delete',2:'replace' } CHANGE_TYPES = ['add','delete','modify','modrdn'] valid_changetype_dict = {} for c in CHANGE_TYPES: valid_changetype_dict[c]=None def is_dn(s): """ returns 1 if s is a LDAP DN """ if s=='': return 1 rm = dn_regex.match(s) return rm!=None and rm.group(0)==s SAFE_STRING_PATTERN = '(^(\000|\n|\r| |:|<)|[\000\n\r\200-\377]+|[ ]+$)' safe_string_re = re.compile(SAFE_STRING_PATTERN) def list_dict(l): """ return a dictionary with all items of l being the keys of the dictionary """ return dict([(i,None) for i in l]) class LDIFWriter: """ Write LDIF entry or change records to file object Copy LDIF input to a file output object containing all data retrieved via URLs """ def __init__(self,output_file,base64_attrs=None,cols=76,line_sep='\n'): """ output_file file object for output base64_attrs list of attribute types to be base64-encoded in any case cols Specifies how many columns a line may have before it's folded into many lines. line_sep String used as line separator """ self._output_file = output_file self._base64_attrs = list_dict([a.lower() for a in (base64_attrs or [])]) self._cols = cols self._line_sep = line_sep self.records_written = 0 def _unfoldLDIFLine(self,line): """ Write string line as one or more folded lines """ # Check maximum line length line_len = len(line) if line_len<=self._cols: self._output_file.write(line) self._output_file.write(self._line_sep) else: # Fold line pos = self._cols self._output_file.write(line[0:min(line_len,self._cols)]) self._output_file.write(self._line_sep) while pos&2 exit 1 else echo "All tests passed. Yay." exit 0 fi python-ldap-2.4.10/Tests/slapd.py0000644000076400001440000003163411242117431017404 0ustar michaelusers00000000000000 """ Utilities for starting up a test slapd server and talking to it with ldapsearch/ldapadd. """ import sys, os, socket, time, subprocess, logging _log = logging.getLogger("slapd") def quote(s): '''Quotes the '"' and '\' characters in a string and surrounds with "..."''' return '"' + s.replace('\\','\\\\').replace('"','\\"') + '"' def mkdirs(path): """Creates the directory path unless it already exists""" if not os.access(os.path.join(path, os.path.curdir), os.F_OK): _log.debug("creating temp directory %s", path) os.mkdir(path) return path def delete_directory_content(path): for dirpath,dirnames,filenames in os.walk(path, topdown=False): for n in filenames: _log.info("remove %s", os.path.join(dirpath, n)) os.remove(os.path.join(dirpath, n)) for n in dirnames: _log.info("rmdir %s", os.path.join(dirpath, n)) os.rmdir(os.path.join(dirpath, n)) LOCALHOST = '127.0.0.1' def find_available_tcp_port(host=LOCALHOST): s = socket.socket() s.bind((host, 0)) port = s.getsockname()[1] s.close() _log.info("Found available port %d", port) return port class Slapd: """ Controller class for a slapd instance, OpenLDAP's server. This class creates a temporary data store for slapd, runs it on a private port, and initialises it with a top-level dc and the root user. When a reference to an instance of this class is lost, the slapd server is shut down. """ _log = logging.getLogger("Slapd") # Use /var/tmp to placate apparmour on Ubuntu: PATH_TMPDIR = "/var/tmp/python-ldap-test" PATH_SBINDIR = "/usr/sbin" PATH_BINDIR = "/usr/bin" PATH_SCHEMA_CORE = "/etc/ldap/schema/core.schema" PATH_LDAPADD = os.path.join(PATH_BINDIR, "ldapadd") PATH_LDAPSEARCH = os.path.join(PATH_BINDIR, "ldapsearch") PATH_SLAPD = os.path.join(PATH_SBINDIR, "slapd") PATH_SLAPTEST = os.path.join(PATH_SBINDIR, "slaptest") # TODO add paths for other OSs def check_paths(cls): """ Checks that the configured executable paths look valid. If they don't, then logs warning messages (not errors). """ for name,path in ( ("slapd", cls.PATH_SLAPD), ("ldapadd", cls.PATH_LDAPADD), ("ldapsearch", cls.PATH_LDAPSEARCH), ): cls._log.debug("checking %s executable at %s", name, path) if not os.access(path, os.X_OK): cls._log.warn("cannot find %s executable at %s", name, path) check_paths = classmethod(check_paths) def __init__(self): self._config = [] self._proc = None self._port = 0 self._tmpdir = self.PATH_TMPDIR self._dn_suffix = "dc=python-ldap,dc=org" self._root_cn = "Manager" self._root_password = "password" self._slapd_debug_level = 0 # Setters def set_port(self, port): self._port = port def set_dn_suffix(self, dn): self._dn_suffix = dn def set_root_cn(self, cn): self._root_cn = cn def set_root_password(self, pw): self._root_password = pw def set_tmpdir(self, path): self._tmpdir = path def set_slapd_debug_level(self, level): self._slapd_debug_level = level def set_debug(self): self._log.setLevel(logging.DEBUG) self.set_slapd_debug_level('Any') # getters def get_url(self): return "ldap://%s:%d/" % self.get_address() def get_address(self): if self._port == 0: self._port = find_available_tcp_port(LOCALHOST) return (LOCALHOST, self._port) def get_dn_suffix(self): return self._dn_suffix def get_root_dn(self): return "cn=" + self._root_cn + "," + self.get_dn_suffix() def get_root_password(self): return self._root_password def get_tmpdir(self): return self._tmpdir def __del__(self): self.stop() def configure(self, cfg): """ Appends slapd.conf configuration lines to cfg. Also re-initializes any backing storage. Feel free to subclass and override this method. """ # Global cfg.append("include " + quote(self.PATH_SCHEMA_CORE)) cfg.append("allow bind_v2") # Database ldif_dir = mkdirs(os.path.join(self.get_tmpdir(), "ldif-data")) delete_directory_content(ldif_dir) # clear it out cfg.append("database ldif") cfg.append("directory " + quote(ldif_dir)) cfg.append("suffix " + quote(self.get_dn_suffix())) cfg.append("rootdn " + quote(self.get_root_dn())) cfg.append("rootpw " + quote(self.get_root_password())) def _write_config(self): """Writes the slapd.conf file out, and returns the path to it.""" path = os.path.join(self._tmpdir, "slapd.conf") ldif_dir = mkdirs(self._tmpdir) if os.access(path, os.F_OK): self._log.debug("deleting existing %s", path) os.remove(path) self._log.debug("writing config to %s", path) file(path, "w").writelines([line + "\n" for line in self._config]) return path def start(self): """ Starts the slapd server process running, and waits for it to come up. """ if self._proc is None: ok = False config_path = None try: self.configure(self._config) self._test_configuration() self._start_slapd() self._wait_for_slapd() ok = True self._log.debug("slapd ready at %s", self.get_url()) self.started() finally: if not ok: if config_path: try: os.remove(config_path) except os.error: pass if self._proc: self.stop() def _start_slapd(self): # Spawns/forks the slapd process config_path = self._write_config() self._log.info("starting slapd") self._proc = subprocess.Popen([self.PATH_SLAPD, "-f", config_path, "-h", self.get_url(), "-d", str(self._slapd_debug_level), ]) self._proc_config = config_path def _wait_for_slapd(self): # Waits until the LDAP server socket is open, or slapd crashed s = socket.socket() while 1: if self._proc.poll() is not None: self._stopped() raise RuntimeError("slapd exited before opening port") try: self._log.debug("Connecting to %s", repr(self.get_address())) s.connect(self.get_address()) s.close() return except socket.error: time.sleep(1) def stop(self): """Stops the slapd server, and waits for it to terminate""" if self._proc is not None: self._log.debug("stopping slapd") if hasattr(self._proc, 'terminate'): self._proc.terminate() else: import posix, signal posix.kill(self._proc.pid, signal.SIGHUP) #time.sleep(1) #posix.kill(self._proc.pid, signal.SIGTERM) #posix.kill(self._proc.pid, signal.SIGKILL) self.wait() def restart(self): """ Restarts the slapd server; ERASING previous content. Starts the server even it if isn't already running. """ self.stop() self.start() def wait(self): """Waits for the slapd process to terminate by itself.""" if self._proc: self._proc.wait() self._stopped() def _stopped(self): """Called when the slapd server is known to have terminated""" if self._proc is not None: self._log.info("slapd terminated") self._proc = None try: os.remove(self._proc_config) except os.error: self._log.debug("could not remove %s", self._proc_config) def _test_configuration(self): config_path = self._write_config() try: self._log.debug("testing configuration") verboseflag = "-Q" if self._log.isEnabledFor(logging.DEBUG): verboseflag = "-v" p = subprocess.Popen([ self.PATH_SLAPTEST, verboseflag, "-f", config_path ]) if p.wait() != 0: raise RuntimeError("configuration test failed") self._log.debug("configuration seems ok") finally: os.remove(config_path) def ldapadd(self, ldif, extra_args=[]): """Runs ldapadd on this slapd instance, passing it the ldif content""" self._log.debug("adding %s", repr(ldif)) p = subprocess.Popen([self.PATH_LDAPADD, "-x", "-D", self.get_root_dn(), "-w", self.get_root_password(), "-H", self.get_url()] + extra_args, stdin = subprocess.PIPE, stdout=subprocess.PIPE) p.communicate(ldif) if p.wait() != 0: raise RuntimeError("ldapadd process failed") def ldapsearch(self, base=None, filter='(objectClass=*)', attrs=[], scope='sub', extra_args=[]): if base is None: base = self.get_dn_suffix() self._log.debug("ldapsearch filter=%s", repr(filter)) p = subprocess.Popen([self.PATH_LDAPSEARCH, "-x", "-D", self.get_root_dn(), "-w", self.get_root_password(), "-H", self.get_url(), "-b", base, "-s", scope, "-LL", ] + extra_args + [ filter ] + attrs, stdout = subprocess.PIPE) output = p.communicate()[0] if p.wait() != 0: raise RuntimeError("ldapadd process failed") # RFC 2849: LDIF format # unfold lines = [] for l in output.split('\n'): if l.startswith(' '): lines[-1] = lines[-1] + l[1:] elif l == '' and lines and lines[-1] == '': pass # ignore multiple blank lines else: lines.append(l) # Remove comments lines = [l for l in lines if not l.startswith("#")] # Remove leading version and blank line(s) if lines and lines[0] == '': del lines[0] if not lines or lines[0] != 'version: 1': raise RuntimeError("expected 'version: 1', got " + repr(lines[:1])) del lines[0] if lines and lines[0] == '': del lines[0] # ensure the ldif ends with a blank line (unless it is just blank) if lines and lines[-1] != '': lines.append('') objects = [] obj = [] for line in lines: if line == '': # end of an object if obj[0][0] != 'dn': raise RuntimeError("first line not dn", repr(obj)) objects.append((obj[0][1], obj[1:])) obj = [] else: attr,value = line.split(':',2) if value.startswith(': '): value = base64.decodestring(value[2:]) elif value.startswith(' '): value = value[1:] else: raise RuntimeError("bad line: " + repr(line)) obj.append((attr,value)) assert obj == [] return objects def started(self): """ This method is called when the LDAP server has started up and is empty. By default, this method adds the two initial objects, the domain object and the root user object. """ assert self.get_dn_suffix().startswith("dc=") suffix_dc = self.get_dn_suffix().split(',')[0][3:] assert self.get_root_dn().startswith("cn=") assert self.get_root_dn().endswith("," + self.get_dn_suffix()) root_cn = self.get_root_dn().split(',')[0][3:] self._log.debug("adding %s and %s", self.get_dn_suffix(), self.get_root_dn()) self.ldapadd("\n".join([ 'dn: ' + self.get_dn_suffix(), 'objectClass: dcObject', 'objectClass: organization', 'dc: ' + suffix_dc, 'o: ' + suffix_dc, '', 'dn: ' + self.get_root_dn(), 'objectClass: organizationalRole', 'cn: ' + root_cn, '' ])) Slapd.check_paths() if __name__ == '__main__' and sys.argv == ['run']: logging.basicConfig(level=logging.DEBUG) slapd = Slapd() print("Starting slapd...") slapd.start() print("Contents of LDAP server follow:\n") for dn,attrs in slapd.ldapsearch(): print("dn: " + dn) for name,val in attrs: print(name + ": " + val) print("") print(slapd.get_url()) slapd.wait() python-ldap-2.4.10/Tests/t_cext.py0000644000076400001440000005501311242166311017565 0ustar michaelusers00000000000000 import unittest, slapd import _ldap import logging reusable_server = None def get_reusable_server(): global reusable_server if reusable_server is None: reusable_server = slapd.Slapd() return reusable_server class TestLdapCExtension(unittest.TestCase): """Tests the LDAP C Extension module, _ldap. These tests apply only to the _ldap module and bypass the LDAPObject wrapper completely.""" timeout = 3 def _init_server(self, reuse_existing=True): global reusable_server """Sets self.server to a test LDAP server and self.base to its base""" if reuse_existing: server = get_reusable_server() else: server = slapd.Slapd() # private server #server.set_debug() # enables verbose messages server.start() # no effect if already started self.server = server self.base = server.get_dn_suffix() return server def _init(self, reuse_existing=True, bind=True): """Starts a server, and returns a LDAPObject bound to it""" server = self._init_server(reuse_existing) l = _ldap.initialize(server.get_url()) if bind: # Perform a simple bind l.set_option(_ldap.OPT_PROTOCOL_VERSION, _ldap.VERSION3) m = l.simple_bind(server.get_root_dn(), server.get_root_password()) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ONE, self.timeout) self.assertTrue(result, _ldap.RES_BIND) return l def assertNotNone(self, expr, msg=None): self.failIf(expr is None, msg or repr(expr)) def assertNone(self, expr, msg=None): self.failIf(expr is not None, msg or repr(expr)) # Test for the existence of a whole bunch of constants # that the C module is supposed to export def test_constants(self): self.assertEquals(_ldap.PORT, 389) self.assertEquals(_ldap.VERSION1, 1) self.assertEquals(_ldap.VERSION2, 2) self.assertEquals(_ldap.VERSION3, 3) # constants for result3() self.assertEquals(_ldap.RES_BIND, 0x61) self.assertEquals(_ldap.RES_SEARCH_ENTRY, 0x64) self.assertEquals(_ldap.RES_SEARCH_RESULT, 0x65) self.assertEquals(_ldap.RES_MODIFY, 0x67) self.assertEquals(_ldap.RES_ADD, 0x69) self.assertEquals(_ldap.RES_DELETE, 0x6b) self.assertEquals(_ldap.RES_MODRDN, 0x6d) self.assertEquals(_ldap.RES_COMPARE, 0x6f) self.assertEquals(_ldap.RES_SEARCH_REFERENCE, 0x73) # v3 self.assertEquals(_ldap.RES_EXTENDED, 0x78) # v3 #self.assertEquals(_ldap.RES_INTERMEDIATE, 0x79) # v3 self.assertNotNone(_ldap.RES_ANY) self.assertNotNone(_ldap.RES_UNSOLICITED) self.assertNotNone(_ldap.AUTH_NONE) self.assertNotNone(_ldap.AUTH_SIMPLE) self.assertNotNone(_ldap.SCOPE_BASE) self.assertNotNone(_ldap.SCOPE_ONELEVEL) self.assertNotNone(_ldap.SCOPE_SUBTREE) self.assertNotNone(_ldap.MOD_ADD) self.assertNotNone(_ldap.MOD_DELETE) self.assertNotNone(_ldap.MOD_REPLACE) self.assertNotNone(_ldap.MOD_INCREMENT) self.assertNotNone(_ldap.MOD_BVALUES) # for result3() self.assertNotNone(_ldap.MSG_ONE) self.assertNotNone(_ldap.MSG_ALL) self.assertNotNone(_ldap.MSG_RECEIVED) # for OPT_DEFEF self.assertNotNone(_ldap.DEREF_NEVER) self.assertNotNone(_ldap.DEREF_SEARCHING) self.assertNotNone(_ldap.DEREF_FINDING) self.assertNotNone(_ldap.DEREF_ALWAYS) # for OPT_SIZELIMIT, OPT_TIMELIMIT self.assertNotNone(_ldap.NO_LIMIT) # standard options self.assertNotNone(_ldap.OPT_API_INFO) self.assertNotNone(_ldap.OPT_DEREF) self.assertNotNone(_ldap.OPT_SIZELIMIT) self.assertNotNone(_ldap.OPT_TIMELIMIT) self.assertNotNone(_ldap.OPT_REFERRALS) self.assertNotNone(_ldap.OPT_RESTART) self.assertNotNone(_ldap.OPT_PROTOCOL_VERSION) self.assertNotNone(_ldap.OPT_SERVER_CONTROLS) self.assertNotNone(_ldap.OPT_CLIENT_CONTROLS) self.assertNotNone(_ldap.OPT_API_FEATURE_INFO) self.assertNotNone(_ldap.OPT_HOST_NAME) self.assertNotNone(_ldap.OPT_ERROR_NUMBER) # = OPT_RESULT_CODE self.assertNotNone(_ldap.OPT_ERROR_STRING) # = OPT_DIAGNOSITIC_MESSAGE self.assertNotNone(_ldap.OPT_MATCHED_DN) # OpenLDAP specific self.assertNotNone(_ldap.OPT_DEBUG_LEVEL) self.assertNotNone(_ldap.OPT_TIMEOUT) self.assertNotNone(_ldap.OPT_REFHOPLIMIT) self.assertNotNone(_ldap.OPT_NETWORK_TIMEOUT) self.assertNotNone(_ldap.OPT_URI) #self.assertNotNone(_ldap.OPT_REFERRAL_URLS) #self.assertNotNone(_ldap.OPT_SOCKBUF) #self.assertNotNone(_ldap.OPT_DEFBASE) #self.assertNotNone(_ldap.OPT_CONNECT_ASYNC) # str2dn() self.assertNotNone(_ldap.DN_FORMAT_LDAP) self.assertNotNone(_ldap.DN_FORMAT_LDAPV3) self.assertNotNone(_ldap.DN_FORMAT_LDAPV2) self.assertNotNone(_ldap.DN_FORMAT_DCE) self.assertNotNone(_ldap.DN_FORMAT_UFN) self.assertNotNone(_ldap.DN_FORMAT_AD_CANONICAL) self.assertNotNone(_ldap.DN_FORMAT_MASK) self.assertNotNone(_ldap.DN_PRETTY) self.assertNotNone(_ldap.DN_SKIP) self.assertNotNone(_ldap.DN_P_NOLEADTRAILSPACES) self.assertNotNone(_ldap.DN_P_NOSPACEAFTERRDN) self.assertNotNone(_ldap.DN_PEDANTIC) self.assertNotNone(_ldap.AVA_NULL) self.assertNotNone(_ldap.AVA_STRING) self.assertNotNone(_ldap.AVA_BINARY) self.assertNotNone(_ldap.AVA_NONPRINTABLE) # these two constants are pointless? XXX self.assertEquals(_ldap.LDAP_OPT_ON, 1) self.assertEquals(_ldap.LDAP_OPT_OFF, 0) # these constants useless after ldap_url_parse() was dropped XXX self.assertNotNone(_ldap.URL_ERR_BADSCOPE) self.assertNotNone(_ldap.URL_ERR_MEM) def test_simple_bind(self): l = self._init() def test_simple_anonymous_bind(self): l = self._init(bind=False) m = l.simple_bind("", "") result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertTrue(result, _ldap.RES_BIND) self.assertEquals(msgid, m) self.assertEquals(pmsg, []) self.assertEquals(ctrls, []) # see if we can get the rootdse while we're here m = l.search_ext("", _ldap.SCOPE_BASE, '(objectClass=*)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_SEARCH_RESULT) self.assertEquals(pmsg[0][0], "") # rootDSE has no dn self.assertEquals(msgid, m) self.assertTrue(pmsg[0][1].has_key('objectClass')) def test_unbind(self): l = self._init() m = l.unbind_ext() self.assertNone(m) # Second attempt to unbind should yield an exception try: l.unbind_ext() except _ldap.error: pass def test_search_ext_individual(self): l = self._init() m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(objectClass=dcObject)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ONE, self.timeout) # Expect to get just one object self.assertEquals(result, _ldap.RES_SEARCH_ENTRY) self.assertEquals(len(pmsg), 1) self.assertEquals(len(pmsg[0]), 2) self.assertEquals(pmsg[0][0], self.base) self.assertEquals(pmsg[0][0], self.base) self.assertTrue('dcObject' in pmsg[0][1]['objectClass']) self.assertTrue('organization' in pmsg[0][1]['objectClass']) self.assertEquals(msgid, m) self.assertEquals(ctrls, []) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ONE, self.timeout) self.assertEquals(result, _ldap.RES_SEARCH_RESULT) self.assertEquals(pmsg, []) self.assertEquals(msgid, m) self.assertEquals(ctrls, []) def test_abandon(self): l = self._init() m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(objectClass=*)') ret = l.abandon_ext(m) self.assertNone(ret) got_timeout = False try: r = l.result3(m, _ldap.MSG_ALL, 0.3) # (timeout /could/ be longer) except _ldap.TIMEOUT, e: got_timeout = True self.assertTrue(got_timeout) def test_search_ext_all(self): l = self._init() m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(objectClass=*)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) # Expect to get some objects self.assertEquals(result, _ldap.RES_SEARCH_RESULT) self.assertTrue(len(pmsg) >= 2) self.assertEquals(msgid, m) self.assertEquals(ctrls, []) def test_add(self): l = self._init() m = l.add_ext("cn=Foo," + self.base, [ ('objectClass','organizationalRole'), ('cn', 'Foo'), ('description', 'testing'), ]) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_ADD) self.assertEquals(pmsg, []) self.assertEquals(msgid, m) self.assertEquals(ctrls, []) # search for it back m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(cn=Foo)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) # Expect to get the objects self.assertEquals(result, _ldap.RES_SEARCH_RESULT) self.assertEquals(len(pmsg), 1) self.assertEquals(msgid, m) self.assertEquals(ctrls, []) self.assertEquals(pmsg[0], ('cn=Foo,'+self.base, { 'objectClass': ['organizationalRole'], 'cn': ['Foo'], 'description': ['testing'] })) def test_compare(self): l = self._init() # first, add an object with a field we can compare on dn = "cn=CompareTest," + self.base m = l.add_ext(dn, [ ('objectClass','person'), ('sn', 'CompareTest'), ('cn', 'CompareTest'), ('userPassword', 'the_password'), ]) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_ADD) # try a false compare m = l.compare_ext(dn, "userPassword", "bad_string") compared_false = False try: r = l.result3(m, _ldap.MSG_ALL, self.timeout) self.fail(repr(r)) except _ldap.COMPARE_FALSE: compared_false = True self.assertTrue(compared_false) # try a true compare m = l.compare_ext(dn, "userPassword", "the_password") compared_true = False try: r = l.result3(m, _ldap.MSG_ALL, self.timeout) self.fail(repr(r)) except _ldap.COMPARE_TRUE: compared_true = True self.assertTrue(compared_true) m = l.compare_ext(dn, "badAttribute", "ignoreme") raised_error = False try: r = l.result3(m, _ldap.MSG_ALL, self.timeout) self.fail(repr(r)) except _ldap.error: raised_error = True self.assertTrue(raised_error) def test_delete_no_such_object(self): l = self._init() # try deleting an object that doesn't exist not_found = False m = l.delete_ext("cn=DoesNotExist,"+self.base) try: r = l.result3(m, _ldap.MSG_ALL, self.timeout) self.fail(r) except _ldap.NO_SUCH_OBJECT: not_found = True self.assertTrue(not_found) def test_delete(self): l = self._init() # first, add an object we will delete dn = "cn=Deleteme,"+self.base m = l.add_ext(dn, [ ('objectClass','organizationalRole'), ('cn', 'Deleteme'), ]) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_ADD) m = l.delete_ext(dn) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_DELETE) self.assertEquals(msgid, m) self.assertEquals(pmsg, []) self.assertEquals(ctrls, []) def test_modify_no_such_object(self): l = self._init() # try deleting an object that doesn't exist not_found = False m = l.modify_ext("cn=DoesNotExist,"+self.base, [ (_ldap.MOD_ADD, 'description', ['blah']), ]) try: r = l.result3(m, _ldap.MSG_ALL, self.timeout) self.fail(r) except _ldap.NO_SUCH_OBJECT: not_found = True self.assertTrue(not_found) def DISABLED_test_modify_no_such_object_empty_attrs(self): # XXX ldif-backend for slapd appears broken??? l = self._init() # try deleting an object that doesn't exist m = l.modify_ext("cn=DoesNotExist,"+self.base, [ (_ldap.MOD_ADD, 'description', []), ]) self.assertTrue(isinstance(m, int)) r = l.result3(m, _ldap.MSG_ALL, self.timeout) # what should happen?? self.fail(r) def test_modify(self): l = self._init() # first, add an object we will delete dn = "cn=AddToMe,"+self.base m = l.add_ext(dn, [ ('objectClass','person'), ('cn', 'AddToMe'), ('sn', 'Modify'), ('description', 'a description'), ]) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_ADD) m = l.modify_ext(dn, [ (_ldap.MOD_ADD, 'description', ['b desc', 'c desc']), ]) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_MODIFY) self.assertEquals(pmsg, []) self.assertEquals(msgid, m) self.assertEquals(ctrls, []) # search for it back m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(cn=AddToMe)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) # Expect to get the objects self.assertEquals(result, _ldap.RES_SEARCH_RESULT) self.assertEquals(len(pmsg), 1) self.assertEquals(msgid, m) self.assertEquals(ctrls, []) self.assertEquals(pmsg[0][0], dn) d = list(pmsg[0][1]['description']) d.sort() self.assertEquals(d, ['a description', 'b desc', 'c desc']) def test_rename(self): l = self._init() dn = "cn=RenameMe,"+self.base m = l.add_ext(dn, [ ('objectClass','organizationalRole'), ('cn', 'RenameMe'), ]) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_ADD) # do the rename with same parent m = l.rename(dn, "cn=IAmRenamed") result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_MODRDN) self.assertEquals(msgid, m) self.assertEquals(pmsg, []) self.assertEquals(ctrls, []) # make sure the old one is gone m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(cn=RenameMe)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_SEARCH_RESULT) self.assertEquals(len(pmsg), 0) # expect no results self.assertEquals(msgid, m) self.assertEquals(ctrls, []) # check that the new one looks right dn2 = "cn=IAmRenamed,"+self.base m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(cn=IAmRenamed)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_SEARCH_RESULT) self.assertEquals(msgid, m) self.assertEquals(ctrls, []) self.assertEquals(len(pmsg), 1) self.assertEquals(pmsg[0][0], dn2) self.assertEquals(pmsg[0][1]['cn'], ['IAmRenamed']) # create the container containerDn = "ou=RenameContainer,"+self.base m = l.add_ext(containerDn, [ ('objectClass','organizationalUnit'), ('ou', 'RenameContainer'), ]) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_ADD) # WORKAROUND bug in slapd. (Without an existing child, # renames into a container object do not work for the ldif backend, # the renamed object appears to be deleted, not moved.) # see http://www.openldap.org/its/index.cgi/Software%20Bugs?id=5408 m = l.add_ext("cn=Bogus," + containerDn, [ ('objectClass','organizationalRole'), ('cn', 'Bogus'), ]) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_ADD) # now rename from dn2 to the conater dn3 = "cn=IAmRenamedAgain," + containerDn # Now try renaming dn2 across container (simultaneous name change) m = l.rename(dn2, "cn=IAmRenamedAgain", containerDn) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_MODRDN) self.assertEquals(msgid, m) self.assertEquals(pmsg, []) self.assertEquals(ctrls, []) # make sure dn2 is gone m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(cn=IAmRenamed)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_SEARCH_RESULT) self.assertEquals(len(pmsg), 0) # expect no results self.assertEquals(msgid, m) self.assertEquals(ctrls, []) m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(objectClass=*)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) # make sure dn3 is there m = l.search_ext(self.base, _ldap.SCOPE_SUBTREE, '(cn=IAmRenamedAgain)') result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_SEARCH_RESULT) self.assertEquals(msgid, m) self.assertEquals(ctrls, []) self.assertEquals(len(pmsg), 1) self.assertEquals(pmsg[0][0], dn3) self.assertEquals(pmsg[0][1]['cn'], ['IAmRenamedAgain']) def test_whoami(self): l = self._init() r = l.whoami_s() self.assertEquals("dn:" + self.server.get_root_dn(), r) def test_whoami_unbound(self): l = self._init(bind=False) l.set_option(_ldap.OPT_PROTOCOL_VERSION, _ldap.VERSION3) r = l.whoami_s() self.assertEquals("", r) def test_whoami_anonymous(self): l = self._init(bind=False) l.set_option(_ldap.OPT_PROTOCOL_VERSION, _ldap.VERSION3) # Anonymous bind m = l.simple_bind("", "") result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertTrue(result, _ldap.RES_BIND) r = l.whoami_s() self.assertEquals("", r) def test_passwd(self): l = self._init() # first, create a user to change password on dn = "cn=PasswordTest," + self.base m = l.add_ext(dn, [ ('objectClass','person'), ('sn', 'PasswordTest'), ('cn', 'PasswordTest'), ('userPassword', 'initial'), ]) result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(result, _ldap.RES_ADD) # try changing password with a wrong old-pw m = l.passwd(dn, "bogus", "ignored") try: r = l.result3(m, _ldap.MSG_ALL, self.timeout) self.fail("expected UNWILLING_TO_PERFORM") except _ldap.UNWILLING_TO_PERFORM: pass # try changing password with a correct old-pw m = l.passwd(dn, "initial", "changed") result,pmsg,msgid,ctrls = l.result3(m, _ldap.MSG_ALL, self.timeout) self.assertEquals(msgid, m) self.assertEquals(pmsg, []) self.assertEquals(result, _ldap.RES_EXTENDED) self.assertEquals(ctrls, []) def test_options(self): oldval = _ldap.get_option(_ldap.OPT_PROTOCOL_VERSION) try: try: _ldap.set_option(_ldap.OPT_PROTOCOL_VERSION, "3") self.fail("expected string value to raise a type error") except TypeError: pass _ldap.set_option(_ldap.OPT_PROTOCOL_VERSION, _ldap.VERSION2) v = _ldap.get_option(_ldap.OPT_PROTOCOL_VERSION) self.assertEquals(v, _ldap.VERSION2) _ldap.set_option(_ldap.OPT_PROTOCOL_VERSION, _ldap.VERSION3) v = _ldap.get_option(_ldap.OPT_PROTOCOL_VERSION) self.assertEquals(v, _ldap.VERSION3) finally: _ldap.set_option(_ldap.OPT_PROTOCOL_VERSION, oldval) l = self._init() # Try changing some basic options and checking that they took effect l.set_option(_ldap.OPT_PROTOCOL_VERSION, _ldap.VERSION2) v = l.get_option(_ldap.OPT_PROTOCOL_VERSION) self.assertEquals(v, _ldap.VERSION2) l.set_option(_ldap.OPT_PROTOCOL_VERSION, _ldap.VERSION3) v = l.get_option(_ldap.OPT_PROTOCOL_VERSION) self.assertEquals(v, _ldap.VERSION3) # Try setting options that will yield a known error. try: _ldap.get_option(_ldap.OPT_MATCHED_DN) self.fail("expected ValueError") except ValueError: pass def _require_attr(self, obj, attrname): """Returns true if the attribute exists on the object. This is to allow some tests to be optional, because _ldap is compiled with different properties depending on the underlying C library. This could me made to thrown an exception if you want the tests to be strict.""" if hasattr(obj, attrname): return True #self.fail("required attribute '%s' missing" % attrname) return False def test_sasl(self): l = self._init() if not self._require_attr(l, 'sasl_interactive_bind_s'): # HAVE_SASL return # TODO def test_tls(self): l = self._init() if not self._require_attr(l, 'start_tls_s'): # HAVE_TLS return # TODO def test_cancel(self): l = self._init() if not self._require_attr(l, 'cancel'): # FEATURE_CANCEL return def test_str2dn(self): pass if __name__ == '__main__': unittest.main() python-ldap-2.4.10/Tests/Lib/0000755000076400001440000000000011764172736016450 5ustar michaelusers00000000000000python-ldap-2.4.10/Tests/Lib/ldap/0000755000076400001440000000000011764172736017370 5ustar michaelusers00000000000000python-ldap-2.4.10/Tests/Lib/ldap/schema/0000755000076400001440000000000011764172736020630 5ustar michaelusers00000000000000python-ldap-2.4.10/Tests/Lib/ldap/schema/test_tokenizer.py0000644000076400001440000000256011176114343024241 0ustar michaelusers00000000000000import ldap.schema from ldap.schema.tokenizer import split_tokens,extract_tokens testcases_split_tokens = ( (" BLUBBER DI BLUBB ", ["BLUBBER", "DI", "BLUBB"]), ("BLUBBER DI BLUBB",["BLUBBER","DI","BLUBB"]), ("BLUBBER DI BLUBB ",["BLUBBER","DI","BLUBB"]), ("BLUBBER DI 'BLUBB' ",["BLUBBER","DI","BLUBB"]), ("BLUBBER ( DI ) 'BLUBB' ",["BLUBBER","(","DI",")","BLUBB"]), ("BLUBBER(DI)",["BLUBBER","(","DI",")"]), ("BLUBBER ( DI)",["BLUBBER","(","DI",")"]), ("BLUBBER ''",["BLUBBER",""]), ("( BLUBBER (DI 'BLUBB'))",["(","BLUBBER","(","DI","BLUBB",")",")"]), ("BLUBB (DA$BLAH)",['BLUBB',"(","DA","BLAH",")"]), ("BLUBB ( DA $ BLAH )",['BLUBB',"(","DA","BLAH",")"]), ("BLUBB (DA$ BLAH)",['BLUBB',"(","DA","BLAH",")"]), ("BLUBB (DA $BLAH)",['BLUBB',"(","DA","BLAH",")"]), ("BLUBB 'DA$BLAH'",['BLUBB',"DA$BLAH"]), ("BLUBB DI 'BLU B B ER' DA 'BLAH' ",['BLUBB','DI','BLU B B ER','DA','BLAH']), ("BLUBB DI 'BLU B B ER' DA 'BLAH' LABER",['BLUBB','DI','BLU B B ER','DA','BLAH','LABER']), ("BLUBBER DI 'BLU'BB ER' DA 'BLAH' ", ["BLUBBER", "DI", "BLU'BB ER", "DA", "BLAH"]), # for Oracle ("BLUBB DI 'BLU B B ER'MUST 'BLAH' ",['BLUBB','DI','BLU B B ER','MUST','BLAH']) # for Oracle ) for t,r in testcases_split_tokens: l = ldap.schema.tokenizer.split_tokens(t,{'MUST':None}) if l!=r: print 'String:',repr(t) print '=>',l print 'differs from',r python-ldap-2.4.10/Tests/Lib/ldap/test_modlist.py0000644000076400001440000000614511573150433022446 0ustar michaelusers00000000000000""" Tests for module ldap.modlist """ import ldap from ldap.modlist import addModlist,modifyModlist print '\nTesting function addModlist():' addModlist_tests = [ ( { 'objectClass':['person','pilotPerson'], 'cn':['Michael Str\303\266der','Michael Stroeder'], 'sn':['Str\303\266der'], 'dummy1':[], 'dummy2':['2'], 'dummy3':[''], }, [ ('objectClass',['person','pilotPerson']), ('cn',['Michael Str\303\266der','Michael Stroeder']), ('sn',['Str\303\266der']), ('dummy2',['2']), ('dummy3',['']), ] ), ] for entry,test_modlist in addModlist_tests: test_modlist.sort() result_modlist = addModlist(entry) result_modlist.sort() if test_modlist!=result_modlist: print 'addModlist(%s) returns\n%s\ninstead of\n%s.' % ( repr(entry),repr(result_modlist),repr(test_modlist) ) print '\nTesting function modifyModlist():' modifyModlist_tests = [ ( { 'objectClass':['person','pilotPerson'], 'cn':['Michael Str\303\266der','Michael Stroeder'], 'sn':['Str\303\266der'], 'enum':['a','b','c'], 'c':['DE'], }, { 'objectClass':['person','inetOrgPerson'], 'cn':['Michael Str\303\266der','Michael Stroeder'], 'sn':[], 'enum':['a','b','d'], 'mail':['michael@stroeder.com'], }, [], [ (ldap.MOD_DELETE,'objectClass',None), (ldap.MOD_ADD,'objectClass',['person','inetOrgPerson']), (ldap.MOD_DELETE,'c',None), (ldap.MOD_DELETE,'sn',None), (ldap.MOD_ADD,'mail',['michael@stroeder.com']), (ldap.MOD_DELETE,'enum',None), (ldap.MOD_ADD,'enum',['a','b','d']), ] ), ( { 'c':['DE'], }, { 'c':['FR'], }, [], [ (ldap.MOD_DELETE,'c',None), (ldap.MOD_ADD,'c',['FR']), ] ), # Now a weird test-case for catching all possibilities # of removing an attribute with MOD_DELETE,attr_type,None ( { 'objectClass':['person'], 'cn':[None], 'sn':[''], 'c':['DE'], }, { 'objectClass':[], 'cn':[], 'sn':[None], }, [], [ (ldap.MOD_DELETE,'c',None), (ldap.MOD_DELETE,'objectClass',None), (ldap.MOD_DELETE,'sn',None), ] ), ( { 'objectClass':['person'], 'cn':['Michael Str\303\266der','Michael Stroeder'], 'sn':['Str\303\266der'], 'enum':['a','b','C'], }, { 'objectClass':['Person'], 'cn':['Michael Str\303\266der','Michael Stroeder'], 'sn':[], 'enum':['a','b','c'], }, ['objectClass'], [ (ldap.MOD_DELETE,'sn',None), (ldap.MOD_DELETE,'enum',None), (ldap.MOD_ADD,'enum',['a','b','c']), ] ), ] for old_entry,new_entry,case_ignore_attr_types,test_modlist in modifyModlist_tests: test_modlist.sort() result_modlist = modifyModlist(old_entry,new_entry,case_ignore_attr_types=case_ignore_attr_types) result_modlist.sort() if test_modlist!=result_modlist: print 'modifyModlist(%s,%s) returns\n%s\ninstead of\n%s.' % ( repr(old_entry), repr(new_entry), repr(result_modlist), repr(test_modlist) ) python-ldap-2.4.10/Tests/Lib/test_ldapurl.py0000644000076400001440000001117207524433465021524 0ustar michaelusers00000000000000""" Performes various tests for module ldapurl """ import ldapurl from ldapurl import * print '\nTesting function isLDAPUrl():' is_ldap_url_tests = { # Examples from RFC2255 'ldap:///o=University%20of%20Michigan,c=US':1, 'ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US':1, 'ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,':1, 'ldap://host.com:6666/o=University%20of%20Michigan,':1, 'ldap://ldap.itd.umich.edu/c=GB?objectClass?one':1, 'ldap://ldap.question.com/o=Question%3f,c=US?mail':1, 'ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04)':1, 'ldap:///??sub??bindname=cn=Manager%2co=Foo':1, 'ldap:///??sub??!bindname=cn=Manager%2co=Foo':1, # More examples from various sources 'ldap://ldap.nameflow.net:1389/c%3dDE':1, 'ldap://root.openldap.org/dc=openldap,dc=org':1, 'ldap://root.openldap.org/dc=openldap,dc=org':1, 'ldap://x500.mh.se/o=Mitthogskolan,c=se????1.2.752.58.10.2=T.61':1, 'ldp://root.openldap.org/dc=openldap,dc=org':0, 'ldap://localhost:1389/ou%3DUnstructured%20testing%20tree%2Cdc%3Dstroeder%2Cdc%3Dcom??one':1, } for ldap_url in is_ldap_url_tests.keys(): result_is_ldap_url = isLDAPUrl(ldap_url) if result_is_ldap_url !=is_ldap_url_tests[ldap_url]: print 'isLDAPUrl("%s") returns %d instead of %d.' % ( repr(ldap_url),result_is_ldap_url,is_ldap_url_tests[ldap_url] ) print '\nTesting class LDAPUrl:' parse_ldap_url_tests = [ ( 'ldap://root.openldap.org/dc=openldap,dc=org', LDAPUrl( hostport='root.openldap.org', dn='dc=openldap,dc=org' ) ), ( 'ldap://root.openldap.org/dc%3dboolean%2cdc%3dnet???%28objectClass%3d%2a%29', LDAPUrl( hostport='root.openldap.org', dn='dc=boolean,dc=net', filterstr='(objectClass=*)' ) ), ( 'ldap://root.openldap.org/dc=openldap,dc=org??sub?', LDAPUrl( hostport='root.openldap.org', dn='dc=openldap,dc=org', scope=ldapurl.LDAP_SCOPE_SUBTREE ) ), ( 'ldap://root.openldap.org/dc=openldap,dc=org??one?', LDAPUrl( hostport='root.openldap.org', dn='dc=openldap,dc=org', scope=ldapurl.LDAP_SCOPE_ONELEVEL ) ), ( 'ldap://root.openldap.org/dc=openldap,dc=org??base?', LDAPUrl( hostport='root.openldap.org', dn='dc=openldap,dc=org', scope=ldapurl.LDAP_SCOPE_BASE ) ), ( 'ldap://x500.mh.se/o=Mitthogskolan,c=se????1.2.752.58.10.2=T.61', LDAPUrl( hostport='x500.mh.se', dn='o=Mitthogskolan,c=se', extensions=LDAPUrlExtensions({ '1.2.752.58.10.2':ldapurl.LDAPUrlExtension( critical=0,extype='1.2.752.58.10.2',exvalue='T.61' ) }) ) ), ( 'ldap://localhost:12345/dc=stroeder,dc=com????!bindname=cn=Michael%2Cdc=stroeder%2Cdc=com,!X-BINDPW=secretpassword', LDAPUrl( hostport='localhost:12345', dn='dc=stroeder,dc=com', extensions=LDAPUrlExtensions({ 'bindname':LDAPUrlExtension( critical=1,extype='bindname',exvalue='cn=Michael,dc=stroeder,dc=com' ), 'X-BINDPW':LDAPUrlExtension( critical=1,extype='X-BINDPW',exvalue='secretpassword' ), }), ) ), ( 'ldap://localhost:54321/dc=stroeder,dc=com????bindname=cn=Michael%2Cdc=stroeder%2Cdc=com,X-BINDPW=secretpassword', LDAPUrl( hostport='localhost:54321', dn='dc=stroeder,dc=com', who='cn=Michael,dc=stroeder,dc=com', cred='secretpassword' ) ), ( 'ldaps://localhost:12345/dc=stroeder,dc=com', LDAPUrl( urlscheme='ldaps', hostport='localhost:12345', dn='dc=stroeder,dc=com', ), ), ( 'ldapi://%2ftmp%2fopenldap2-1389/dc=stroeder,dc=com', LDAPUrl( urlscheme='ldapi', hostport='/tmp/openldap2-1389', dn='dc=stroeder,dc=com', ), ), ] for ldap_url_str,test_ldap_url_obj in parse_ldap_url_tests: # print '\nTesting LDAP URL:',repr(ldap_url) ldap_url_obj = LDAPUrl(ldapUrl=ldap_url_str) print '#'*72 print test_ldap_url_obj.unparse() if ldap_url_obj.__ne__(test_ldap_url_obj): print '-'*72 print 'Parsing error! Attributes of LDAPUrl(%s) are:\n%s\ninstead of:\n%s' % ( repr(ldap_url_str), repr(ldap_url_obj), repr(test_ldap_url_obj) ) else: print 'Parsing ok' unparsed_ldap_url_str = test_ldap_url_obj.unparse() unparsed_ldap_url_obj = LDAPUrl(ldapUrl=unparsed_ldap_url_str) if unparsed_ldap_url_obj.__ne__(test_ldap_url_obj): print '-'*72 print 'Unparsing error! Attributes of LDAPUrl(%s) are:\n%s\ninstead of:\n%s' % ( repr(unparsed_ldap_url_str), repr(unparsed_ldap_url_obj), repr(test_ldap_url_obj) ) else: print 'Unparsing ok' python-ldap-2.4.10/Tests/search.py0000644000076400001440000000152010772733571017555 0ustar michaelusers00000000000000import sys,pprint,ldap from ldap.ldapobject import LDAPObject from ldapurl import LDAPUrl class MyLDAPUrl(LDAPUrl): attr2extype = { 'who':'bindname', 'cred':'X-BINDPW', 'start_tls':'startTLS', 'trace_level':'trace', } ldap_url = MyLDAPUrl(sys.argv[1]) trace_level = int(ldap_url.trace_level or '0') print '***trace_level',trace_level ldap.trace_level = trace_level l = LDAPObject( ldap_url.initializeUrl(), trace_level=trace_level, ) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS,0) l.simple_bind_s((ldap_url.who or ''),(ldap_url.cred or '')) result = l.search_s( ldap_url.dn, ldap_url.scope or ldap.SCOPE_SUBTREE, ldap_url.filterstr or '(objectClass=*)', ldap_url.attrs or ['*'] ) pprint.pprint(result) print '***DIAGNOSTIC_MESSAGE',repr(l.get_option(ldap.OPT_DIAGNOSTIC_MESSAGE)) l.unbind_s() python-ldap-2.4.10/Build/0000755000076400001440000000000011764172736015677 5ustar michaelusers00000000000000python-ldap-2.4.10/Build/setup.cfg.mingw0000644000076400001440000000173110175512766020636 0ustar michaelusers00000000000000# Win32 setup.cfg # You have to edit this file to reflect your system configuration # # $Id: setup.cfg.mingw,v 1.1 2005/01/25 18:54:46 stroeder Exp $ # Section for compiling the C extension module # for wrapping OpenLDAP 2 libs # Platform: Win32 # Compile environment: MinGW [_ldap] class = OpenLDAP2 maintainer = Mauro Cicognini defines = WIN32 # modify these to fit your local configuration library_dirs = C:/msys/1.0/home/mcicogni/openldap-mingw-build-4/openldap-2.2.18/libraries/libldap_r/.libs C:/msys/1.0/home/mcicogni/openldap-mingw-build-4/openldap-2.2.18/libraries/liblber/.libs C:\msys\1.0\home\mcicogni\openldap-mingw-build-4\openssl-0.9.7e include_dirs = C:/msys/1.0/home/mcicogni/openldap-mingw-build-4/openldap-2.2.18/include extra_compile_args = extra_objects = libs = ldap_r lber ssl crypto ws2_32 gdi32 [build] compiler = mingw32 # Installation options [install] compile = 1 optimize = 1 record = python-ldap_install.log [bdist_wininst] target_version = 2.4 python-ldap-2.4.10/Build/setup.cfg.suse-linux0000644000076400001440000000161207720643662021631 0ustar michaelusers00000000000000# Example for setup.cfg # You have to edit this file to reflect your system configuation # $Id: setup.cfg.suse-linux,v 1.1 2003/08/20 10:04:34 stroeder Exp $ [_ldap] # Section for compiling the C extension module # for wrapping OpenLDAP 2 libs library_dirs = /usr/lib/sasl2 include_dirs = /usr/include/sasl extra_compile_args = extra_objects = # Example for full-featured SuSE build: # Support for StartTLS/LDAPS, SASL bind and reentrant libldap_r. # This needs recent OpenLDAP 2.0.26+ or 2.1.3+ built with # ./configure --with-cyrus-sasl --with-tls libs = ldap_r lber sasl2 ssl crypto [install] # Installation options compile = 1 optimize = 1 # For SuSE Linux 8.2 [bdist_rpm] provides = python-ldap requires = python openldap2-client openssl cyrus-sasl2 distribution_name = SuSE Linux 8.2 release = 1 packager = Michael Stroeder doc_files = CHANGES README INSTALL TODO Demo/ python-ldap-2.4.10/Build/setup.cfg.win320000644000076400001440000000227607720643662020466 0ustar michaelusers00000000000000# Section for compiling the C extension module # for wrapping OpenLDAP 2 libs # Platform: Win32 # Compile environment: Microsoft Visual Studio .NET 2003 [_ldap] class = OpenLDAP2 defines = WIN32 # Cannot have SSL/TLS support under Win32 for the moment # (OpenLDAP 2.x port is incomplete) libs = olber32 oldap_r ws2_32 libsasl # Set these to your correct Openldap and Cyrus-sasl paths library_dirs = ../openldap/openldap-2.1.22/Release ../openldap/cyrus-sasl/lib include_dirs = ../openldap/openldap-2.1.22/include ../openldap/cyrus-sasl/include # Needs to compile as /MT ("MS libs to use: multithreaded statically-linked") # instead of /MD ("MS libs to use: multithreaded DLL") which is distutils' default # because OpenLDAP libs compile that way, too # This may change, however extra_compile_args = /MT extra_link_args = /NODEFAULTLIB:msvcrt.lib # Pull in SASL DLL as a convenience to end-user (which almost never will have it) # Destination path is a rather crude hack, but site-packages would be created anyway # Set source path to your Cyrus-sasl lib path extra_files = Lib/site-packages:../openldap/cyrus-sasl/lib/libsasl.dll # Installation options [install] compile = 1 record = python-ldap_install.log python-ldap-2.4.10/Build/build-openbsd/0000755000076400001440000000000011764172736020426 5ustar michaelusers00000000000000python-ldap-2.4.10/Build/build-openbsd/Makefile0000644000076400001440000000145307147771567022100 0ustar michaelusers00000000000000# $Id: Makefile,v 1.3 2000/08/20 15:04:23 leonard Exp $ WRKDIST= ${.CURDIR}/../.. VERSION!= sh ${WRKDIST}/Build/version.sh PKGNAME= python-ldap-${VERSION} RUN_DEPENDS= python:lang/python CATEGORIES= misc MAINATINER= leonard@users.sourceforge.net HOMEPAGE= http://python-ldap.sourceforge.net/ FAKE= Yes CONFIGURE_STYLE= gnu SEPARATE_BUILD= Yes EXTRACT_ONLY= CONFIGURE_ARGS+= --with-ldap=${LOCALBASE} do-fetch:; #-- put package on sourceforge ftp server export-ftp: ${PKGFILE} scp ${PKGFILE} python-ldap.sourceforge.net:/home/groups/ftp/pub/python-ldap/${PKGNAME}-${OPSYS}-${ARCH}${PKG_SUFX} post-build: sed -e 's,^${PREFIX}/,,' <${WRKBUILD}/filelist >${WRKBUILD}/filelist2 SED_PLIST+= |sed -e '/^%%filelist/r${WRKBUILD}/filelist2' -e '//d' ALL_TARGET= build filelist .include python-ldap-2.4.10/Build/build-openbsd/pkg/0000755000076400001440000000000011764172736021207 5ustar michaelusers00000000000000python-ldap-2.4.10/Build/build-openbsd/pkg/PLIST0000644000076400001440000000011507147771570022023 0ustar michaelusers00000000000000@comment $OpenBSD$ %%filelist @dirrm lib/python1.6/site-packages/python-ldap python-ldap-2.4.10/Build/build-openbsd/pkg/COMMENT0000644000076400001440000000003107145657634022231 0ustar michaelusers00000000000000Python interface to LDAP python-ldap-2.4.10/Build/build-openbsd/pkg/DESCR0000644000076400001440000000015607145657634021777 0ustar michaelusers00000000000000This Python library provides access to the LDAP (Lightweight Directory Access Protocol) RFC1823 C interface. python-ldap-2.4.10/README0000644000076400001440000000706411660514461015515 0ustar michaelusers00000000000000--------------------------------------- python-ldap: LDAP client API for Python --------------------------------------- What is python-ldap? python-ldap provides an object-oriented API to access LDAP directory servers from Python programs. Mainly it wraps the OpenLDAP client libs for that purpose. Additionally the package contains modules for other LDAP-related stuff (e.g. processing LDIF, LDAPURLs, LDAPv3 sub-schema, etc.). Not included: Direct BER support See INSTALL for version compability See TODO for planned features. Contributors welcome. For module documentation, see: http://www.python-ldap.org/ Quick usage example: import ldap l = ldap.initialize("ldap://my_ldap_server.my_domain") l.simple_bind_s("","") l.search_s("o=My Organisation, c=AU", ldap.SCOPE_SUBTREE, "objectclass=*") See directory Demo/ of source distribution package for more example code. Author(s) contact and documentation: http://www.python-ldap.org/ If you are looking for help, please try the mailing list archives first, then send a question to the mailing list. Be warned that questions will be ignored if they can be trivially answered by referring to the documentation. If you are interested in helping, please contact the mailing list. If you want new features or upgrades, please check the mailing list archives and then enquire about any progress. Acknowledgements: Thanks to Konstantin Chuguev and Steffen Ries for working on support for OpenLDAP 2.0.x features. Thanks to Michael Stroeder for the modules ldif, ldapurl, ldap/schema/*.py, ldap/*.py and ldap/controls/*.py. Thanks to Hans Aschauer for the C wrapper schema and SASL support. Thanks to Mauro Cicognini for the WIN32/MSVC6 bits, and the pre-built WIN32 ldap.pyd. Thanks to Waldemar Osuch for contributing the new-style docs based on reStructuredText. Thanks to Torsten Kurbad for the easy_install support. Thanks to James Andrewartha for significant contribution to Doc/*.tex. Thanks to Rich Megginson for extending support for LDAPv3 controls and adding support for LDAPv3 extended operations. Thanks to Peter Gietz, DAASI for funding some control modules. Thanks to Chris Mikkelson for various fixes and ldap.syncrepl. These very kind people have supplied patches or suggested changes: Federico Di Gregorio John Benninghoff Donn Cave Jason Gunthorpe gurney_j Eric S. Johansson David Margrave Uche Ogbuji Neale Pickett Blake Weston Wido Depping Deepak Giridharagopal Ingo Steuwer Andreas Hasenack Matej Vela Thanks to all the guys on the python-ldap mailing list for their contributions and input into this package. Thanks! We may have missed someone: please mail us if we have omitted your name. $Id: README,v 1.25 2011/10/26 18:43:21 stroeder Exp $ python-ldap-2.4.10/Doc/0000755000076400001440000000000011764172736015345 5ustar michaelusers00000000000000python-ldap-2.4.10/Doc/ldap-dn.rst0000644000076400001440000000745511612335224017412 0ustar michaelusers00000000000000.. % $Id: ldap-dn.rst,v 1.6 2011/07/22 07:43:45 stroeder Exp $ :py:mod:`ldap.dn` LDAP Distinguished Name handling ==================================================== .. py:module:: ldap.dn :synopsis: LDAP Distinguished Name handling. .. moduleauthor:: python-ldap project (see http://www.python-ldap.org/) .. % Author of the module code; .. seealso:: For LDAPv3 DN syntax see: :rfc:`4514` - Lightweight Directory Access Protocol (LDAP): String Representation of Distinguished Names .. seealso:: For deprecated LDAPv2 DN syntax (obsoleted by LDAPv3) see: :rfc:`1779` - A String Representation of Distinguished Names The :mod:`ldap.dn` module defines the following functions: .. function:: escape_dn_chars(s) This function escapes characters in string *s* which are special in LDAP distinguished names. You should use this function when building LDAP DN strings from arbitrary input. .. % -> string .. function:: str2dn(s [, flags=0]) This function takes *s* and breaks it up into its component parts down to AVA level. The optional parameter *flags* describes the DN format of s (see :ref:`ldap-dn-flags`). Note that hex-encoded non-ASCII chars are decoded to the raw bytes. .. % -> list .. function:: dn2str(dn) This function takes a decomposed DN in *dn* and returns a single string. It's the inverse to :func:`str2dn`. Special characters are escaped with the help of function :func:`escape_dn_chars`. .. % -> string .. function:: explode_dn(dn [, notypes=0[, flags=0]]) This function takes *dn* and breaks it up into its component parts. Each part is known as an RDN (Relative Distinguished Name). The optional *notypes* parameter is used to specify that only the RDN values be returned and not their types. The optional parameter *flags* describes the DN format of s (see :ref:`ldap-dn-flags`). This function is emulated by function :func:`str2dn` since the function ldap_explode_dn() in the C library is deprecated. .. % -> list .. function:: explode_rdn(rdn [, notypes=0[, flags=0]]) This function takes a (multi-valued) *rdn* and breaks it up into a list of characteristic attributes. The optional *notypes* parameter is used to specify that only the RDN values be returned and not their types. The optional *flags* parameter describes the DN format of s (see :ref:`ldap-dn-flags`). This function is emulated by function :func:`str2dn` since the function ldap_explode_rdn() in the C library is deprecated. .. % -> list .. _ldap-dn-example: Examples ^^^^^^^^^ Splitting a LDAPv3 DN to AVA level. Note that both examples have the same result but in the first example the non-ASCII chars are passed as is (byte buffer string) whereas in the second example the hex-encoded DN representation are passed to the function. >>> ldap.dn.str2dn('cn=Michael Str\xc3\xb6der,dc=stroeder,dc=com',flags=ldap.DN_FORMAT_LDAPV3) [[('cn', 'Michael Str\xc3\xb6der', 4)], [('dc', 'stroeder', 1)], [('dc', 'com', 1)]] >>> ldap.dn.str2dn('cn=Michael Str\C3\B6der,dc=stroeder,dc=com',flags=ldap.DN_FORMAT_LDAPV3) [[('cn', 'Michael Str\xc3\xb6der', 4)], [('dc', 'stroeder', 1)], [('dc', 'com', 1)]] Splitting a LDAPv2 DN into RDN parts: >>> ldap.dn.explode_dn('cn=Michael Stroeder;dc=stroeder;dc=com',flags=ldap.DN_FORMAT_LDAPV2) ['cn=Michael Stroeder', 'dc=stroeder', 'dc=com'] Splitting a multi-valued RDN: >>> ldap.dn.explode_rdn('cn=Michael Stroeder+mail=michael@stroeder.com',flags=ldap.DN_FORMAT_LDAPV2) ['cn=Michael Stroeder', 'mail=michael@stroeder.com'] Splitting a LDAPv3 DN with a multi-valued RDN into its AVA parts: >>> ldap.dn.str2dn('cn=Michael Stroeder+mail=michael@stroeder.com,dc=stroeder,dc=com') [[('cn', 'Michael Stroeder', 1), ('mail', 'michael@stroeder.com', 1)], [('dc', 'stroeder', 1)], [('dc', 'com', 1)]] python-ldap-2.4.10/Doc/ldif.rst0000644000076400001440000000367611643040601017006 0ustar michaelusers00000000000000.. % $Id: ldif.rst,v 1.8 2011/09/14 18:29:18 stroeder Exp $ ##################################### :mod:`ldif` LDIF parser and generator ##################################### .. py:module:: ldif :synopsis: Parses and generates LDIF files .. moduleauthor:: python-ldap project (see http://www.python-ldap.org/) This module parses and generates LDAP data in the format LDIF. It is implemented in pure Python and does not rely on any non-standard modules. Therefore it can be used stand-alone without the rest of the python-ldap package. .. seealso:: :rfc:`2849` - The LDAP Data Interchange Format (LDIF) - Technical Specification Functions ^^^^^^^^^ .. autofunction:: ldif.CreateLDIF .. autofunction:: ldif.ParseLDIF Classes ^^^^^^^ .. autoclass:: ldif.LDIFWriter .. autoclass:: ldif.LDIFParser .. autoclass:: LDIFRecordList .. autoclass:: LDIFCopy .. _ldif-example: Example ^^^^^^^ The following example demonstrates how to write LDIF output of an LDAP entry with :mod:`ldif` module. >>> import sys,ldif >>> entry={'objectClass':['top','person'],'cn':['Michael Stroeder'],'sn':['Stroeder']} >>> dn='cn=Michael Stroeder,ou=Test' >>> ldif_writer=ldif.LDIFWriter(sys.stdout) >>> ldif_writer.unparse(dn,entry) dn: cn=Michael Stroeder,ou=Test cn: Michael Stroeder objectClass: top objectClass: person sn: Stroeder The following example demonstrates how to parse an LDIF file with :mod:`ldif` module, skip some entries and write the result to stdout. :: import sys from ldif import LDIFParser,LDIFWriter SKIP_DN = ["uid=foo,ou=People,dc=example,dc=com", "uid=bar,ou=People,dc=example,dc=com"] class MyLDIF(LDIFParser): def __init__(self,input,output): LDIFParser.__init__(self,input) self.writer = LDIFWriter(output) def handle(self,dn,entry): if dn in SKIP_DN: return self.writer.unparse(dn,entry) parser = MyLDIF(open("input.ldif", 'rb'), sys.stdout) parser.parse() python-ldap-2.4.10/Doc/.cvsignore0000644000076400001440000000005011026416722017323 0ustar michaelusers00000000000000python-ldap.pdf python-ldap.ps *.bck *~ python-ldap-2.4.10/Doc/ldap-resiter.rst0000644000076400001440000000264011643040601020451 0ustar michaelusers00000000000000.. % $Id: ldap-resiter.rst,v 1.5 2011/08/27 15:43:06 stroeder Exp $ :py:mod:`ldap.resiter` Generator for stream-processing of large search results ============================================================================== .. py:module:: ldap.resiter :synopsis: Generator for stream-processing of large search results. .. moduleauthor:: python-ldap project (see http://www.python-ldap.org/) .. _ldap.resiter-classes: .. py:class:: ResultProcessor This is a mix-in class to be used with class :py:class:`ldap.LDAPObject` or derived classes which has these methods: .. automethod:: ldap.resiter.ResultProcessor.allresults .. _ldap.resiter-example: Examples ======== .. _ldap.resiter.ResultProcessor-example: Using ldap.resiter.ResultProcessor ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This example demonstrates how to use mix-in class ldap.resiter.ResultProcessor for retrieving results formerly requested with :py:meth:`ldap.LDAPObject.search()` and processing them in a for-loop. :: import sys,ldap,ldap.resiter class MyLDAPObject(ldap.ldapobject.LDAPObject,ldap.resiter.ResultProcessor): pass l = MyLDAPObject('ldap://localhost') # Asynchronous search method msg_id = l.search('dc=stroeder,dc=com',ldap.SCOPE_SUBTREE,'(objectClass=*)') for res_type,res_data,res_msgid,res_controls in l.allresults(msg_id): for dn,entry in res_data: # process dn and entry print dn,entry['objectClass'] python-ldap-2.4.10/Doc/Makefile0000644000076400001440000000432610777372473017015 0ustar michaelusers00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html web htmlhelp latex changes linkcheck help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " web to make files usable by Sphinx.web" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf .build/* html: mkdir -p .build/html .build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html @echo @echo "Build finished. The HTML pages are in .build/html." web: mkdir -p .build/web .build/doctrees $(SPHINXBUILD) -b web $(ALLSPHINXOPTS) .build/web @echo @echo "Build finished; now you can run" @echo " python -m sphinx.web .build/web" @echo "to start the server." htmlhelp: mkdir -p .build/htmlhelp .build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in .build/htmlhelp." latex: mkdir -p .build/latex .build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex @echo @echo "Build finished; the LaTeX files are in .build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p .build/changes .build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes @echo @echo "The overview file is in .build/changes." linkcheck: mkdir -p .build/linkcheck .build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in .build/linkcheck/output.txt." python-ldap-2.4.10/Doc/conf.py0000644000076400001440000001041411660514461016632 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- # # python-ldap documentation build configuration file, created by # sphinx-quickstart on Sat Mar 29 15:08:17 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. # # $Id: conf.py,v 1.17 2011/10/14 11:48:31 stroeder Exp $ import sys # If your extensions are in another directory, add it here. #sys.path.append('some/directory') # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['.templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'python-ldap' copyright = '2008-2011, python-ldap project team' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = '2.4' # The full version, including alpha/beta/rc tags. release = '2.4.4.0' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['.static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Content template for the index page. #html_index = '' # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. html_use_modindex = True # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # Output file base name for HTML help builder. htmlhelp_basename = 'python-ldap-doc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [('index', 'python-ldap.tex', 'python-ldap Documentation', 'python-ldap project', 'manual')] # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. latex_use_modindex = True python-ldap-2.4.10/Doc/ldapurl.rst0000644000076400001440000000566411612335224017536 0ustar michaelusers00000000000000.. % $Id: ldapurl.rst,v 1.9 2011/07/22 13:27:01 stroeder Exp $ ################################### :py:mod:`ldapurl` LDAP URL handling ################################### .. py:module:: ldapurl :synopsis: Parses and generates LDAP URLs .. moduleauthor:: python-ldap project (see http://www.python-ldap.org/) This module parses and generates LDAP URLs. It is implemented in pure Python and does not rely on any non-standard modules. Therefore it can be used stand- alone without the rest of the python-ldap package. Compability note: This module has been solely tested on Python 2.x and above. .. seealso:: :rfc:`4516` - The LDAP URL Format Constants ========= The :mod:`ldapurl` module exports the following constants: .. py:data:: SEARCH_SCOPE This dictionary maps a search scope string identifier to the corresponding integer value used with search operations in :mod:`ldap`. .. py:data:: SEARCH_SCOPE_STR This dictionary is the inverse to :const:`SEARCH_SCOPE`. It maps a search scope integer value to the corresponding string identifier used in a LDAP URL string representation. .. py:data:: LDAP_SCOPE_BASE .. py:data:: LDAP_SCOPE_ONELEVEL .. py:data:: LDAP_SCOPE_SUBTREE Functions ========= .. autofunction:: ldapurl.isLDAPUrl .. autofunction:: ldapurl.ldapUrlEscape Classes ======= .. _ldapurl-ldapurl: LDAP URLs ^^^^^^^^^ A :py:class:`LDAPUrl` object represents a complete LDAP URL. .. autoclass:: ldapurl.LDAPUrl LDAP URL extensions ^^^^^^^^^^^^^^^^^^^ A :py:class:`LDAPUrlExtension` object represents a single LDAP URL extension whereas :py:class:`LDAPUrlExtensions` represents a list of LDAP URL extensions. .. _ldapurl-ldapurlextension: .. autoclass:: ldapurl.LDAPUrlExtension .. _ldapurl-ldapurlextensions: .. autoclass:: ldapurl.LDAPUrlExtensions .. _ldapurl-example: Example ^^^^^^^ Important security advice: For security reasons you shouldn't specify passwords in LDAP URLs unless you really know what you are doing. The following example demonstrates how to parse a LDAP URL with :mod:`ldapurl` module. >>> import ldapurl >>> ldap_url = ldapurl.LDAPUrl('ldap://localhost:1389/dc=stroeder,dc=com?cn,mail???bindname=cn=Michael%2cdc=stroeder%2cdc=com,X-BINDPW=secret') >>> # Using the parsed LDAP URL by reading the class attributes >>> ldap_url.dn 'dc=stroeder,dc=com' >>> ldap_url.hostport 'localhost:1389' >>> ldap_url.attrs ['cn','mail'] >>> ldap_url.filterstr '(objectclass=*)' >>> ldap_url.who 'cn=Michael,dc=stroeder,dc=com' >>> ldap_url.cred 'secret' >>> ldap_url.scope 0 The following example demonstrates how to generate a LDAP URL with \module{ldapurl} module. >>> import ldapurl >>> ldap_url = ldapurl.LDAPUrl(hostport='localhost:1389',dn='dc=stroeder,dc=com',attrs=['cn','mail'],who='cn=Michael,dc=stroeder,dc=com',cred='secret') >>> ldap_url.unparse() 'ldap://localhost:1389/dc=stroeder,dc=com?cn,mail?base?(objectclass=*)?bindname=cn=Michael%2Cdc=stroeder%2Cdc=com,X-BINDPW=secret' python-ldap-2.4.10/Doc/ldap-syncrepl.rst0000644000076400001440000000123011652061147020635 0ustar michaelusers00000000000000.. % $Id: ldap-syncrepl.rst,v 1.1 2011/10/26 19:41:27 stroeder Exp $ ******************************************************************** :py:mod:`ldap.syncrepl` Implementation of a syncrepl consumer ******************************************************************** .. py:module:: ldap.syncrepl :synopsis: Implementation of a syncrepl consumer .. seealso:: :rfc:`4533` - Lightweight Directory Access Protocol (v3): Content Synchronization Operation This requires :py:mod:`pyasn1` and :py:mod:`pyasn1_modules` to be installed. Classes ======= This module defines the following classes: .. autoclass:: ldap.syncrepl.SyncreplConsumer :members: python-ldap-2.4.10/Doc/dsml.rst0000644000076400001440000000125011614223106017012 0ustar michaelusers00000000000000.. % $Id: dsml.rst,v 1.5 2011/07/28 09:05:10 stroeder Exp $ *************************************** :mod:`dsml` DSMLv1 parser and generator *************************************** .. :py:module:: dsml :synopsis: Parses and generates DSMLv1 files .. moduleauthor:: python-ldap project (see http://www.python-ldap.org/) This module parses and generates LDAP data in the format DSMLv1. It is implemented in pure Python and does not rely on any non-standard modules. Therefore it can be used stand-alone without the rest of the python-ldap package. .. autoclass:: dsml.DSMLWriter :members: .. autoclass:: dsml.DSMLParser :members: .. _dsml-example: Example ======= python-ldap-2.4.10/Doc/ldap-controls.rst0000644000076400001440000001252211706071063020645 0ustar michaelusers00000000000000.. % $Id: ldap-controls.rst,v 1.10 2011/07/23 08:15:38 stroeder Exp $ ********************************************************************* :py:mod:`ldap.controls` High-level access to LDAPv3 extended controls ********************************************************************* .. py:module:: ldap.controls :synopsis: High-level access to LDAPv3 extended controls. .. moduleauthor:: python-ldap project (see http://www.python-ldap.org/) Variables ========= .. py:data:: KNOWN_RESPONSE_CONTROLS Dictionary mapping the OIDs of known response controls to the accompanying :py:class:`ResponseControl` classes. This is used by :py:func:`DecodeControlTuples` to automatically decode control values. Calling application can also register their custom :py:class:`ResponseControl` classes in this dictionary possibly overriding pre-registered classes. Classes ======= This module defines the following classes: .. autoclass:: ldap.controls.RequestControl :members: .. autoclass:: ldap.controls.ResponseControl :members: .. autoclass:: ldap.controls.LDAPControl :members: Functions ========= This module defines the following functions: .. autofunction:: ldap.controls.RequestControlTuples .. autofunction:: ldap.controls.DecodeControlTuples Sub-modules =========== Various sub-modules implement specific LDAPv3 extended controls. The classes therein are derived from the base-classes :py:class:`ldap.controls.RequestControl`, :py:class:`ldap.controls.ResponseControl` or :py:class:`ldap.controls.LDAPControl`. Some of them require :py:mod:`pyasn1` and :py:mod:`pyasn1_modules` to be installed: Usually the names of the method arguments and the class attributes match the ASN.1 identifiers used in the specification. So looking at the referenced RFC or Internet-Draft is very helpful to understand the API. :py:mod:`ldap.controls.simple` Very simple controls ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. py:module:: ldap.controls.simple :synopsis: simple request and response controls implemented in pure Python .. autoclass:: ldap.controls.simple.ValueLessRequestControl :members: .. autoclass:: ldap.controls.simple.OctetStringInteger :members: .. autoclass:: ldap.controls.simple.BooleanControl :members: .. autoclass:: ldap.controls.simple.ManageDSAITControl :members: .. seealso:: :rfc:`3296` - Named Subordinate References in Lightweight Directory Access Protocol (LDAP) Directories .. autoclass:: ldap.controls.simple.RelaxRulesControl :members: .. seealso:: http://tools.ietf.org/draft/draft-zeilenga-ldap-relax/ .. autoclass:: ldap.controls.simple.ProxyAuthzControl :members: .. seealso:: :rfc:`4370` - Lightweight Directory Access Protocol (LDAP): Proxied Authorization Control .. autoclass:: ldap.controls.simple.AuthorizationIdentityControl :members: .. seealso:: :rfc:`3829` - Lightweight Directory Access Protocol (LDAP): Authorization Identity Request and Response Controls .. autoclass:: ldap.controls.simple.GetEffectiveRightsControl :members: :py:mod:`ldap.controls.libldap` Various controls implemented in OpenLDAP libs ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. py:module:: ldap.controls.libldap :synopsis: request and response controls implemented by OpenLDAP libs This module wraps C functions in OpenLDAP client libs which implement various request and response controls into Python classes. .. autoclass:: ldap.controls.libldap.AssertionControl :members: .. seealso:: :rfc:`4528` - Lightweight Directory Access Protocol (LDAP) Assertion Control .. autoclass:: ldap.controls.libldap.MatchedValuesControl :members: .. seealso:: :rfc:`3876` - Returning Matched Values with the Lightweight Directory Access Protocol version 3 (LDAPv3) .. autoclass:: ldap.controls.libldap.SimplePagedResultsControl :members: .. seealso:: :rfc:`2696` - LDAP Control Extension for Simple Paged Results Manipulation :py:mod:`ldap.controls.psearch` LDAP Persistent Search ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. py:module:: ldap.controls.psearch :synopsis: request and response controls for LDAP persistent search This module implements request and response controls for LDAP persistent search. .. seealso:: http://tools.ietf.org/html/draft-ietf-ldapext-psearch .. autoclass:: ldap.controls.psearch.PersistentSearchControl :members: .. autoclass:: ldap.controls.psearch.EntryChangeNotificationControl :members: :py:mod:`ldap.controls.sessiontrack` Session tracking control ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. py:module:: ldap.controls.sessiontrack :synopsis: request control for session tracking .. seealso:: http://tools.ietf.org/html/draft-wahl-ldap-session .. autoclass:: ldap.controls.sessiontrack.SessionTrackingControl :members: :py:mod:`ldap.controls.readentry` Read entry control ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. py:module:: ldap.controls.readentry :synopsis: read entryrequest and response controls .. seealso:: :rfc:`4527` - Lightweight Directory Access Protocol (LDAP): Read Entry Controls .. autoclass:: ldap.controls.readentry.ReadEntryControl :members: .. autoclass:: ldap.controls.readentry.PreReadControl :members: .. autoclass:: ldap.controls.readentry.PostReadControl :members: python-ldap-2.4.10/Doc/ldap-async.rst0000644000076400001440000000457411643040601020121 0ustar michaelusers00000000000000.. % $Id: ldap-async.rst,v 1.4 2011/07/28 08:52:01 stroeder Exp $ ************************************************************** :py:mod:`ldap.async` Stream-processing of large search results ************************************************************** .. py:module:: ldap.async :synopsis: Framework for stream-processing of large search results. With newer Python versions one might want to consider using :py:mod:`ldap.resiter` instead. Classes ======= .. autoclass:: ldap.async.AsyncSearchHandler :members: .. autoclass:: ldap.async.List :members: .. autoclass:: ldap.async.Dict :members: .. autoclass:: ldap.async.IndexedDict :members: .. autoclass:: ldap.async.LDIFWriter :members: .. autoclass:: ldap.async.DSMLWriter :members: .. _ldap.async-example: Examples ======== .. _ldap.async-example.List: Using ldap.async.List ^^^^^^^^^^^^^^^^^^^^^ This example demonstrates how to use class ldap.async.List for retrieving partial search results even though the exception :exc:`ldap.SIZELIMIT_EXCEEDED` was raised because a server side limit was hit. :: import sys,ldap,ldap.async s = ldap.async.List( ldap.initialize('ldap://localhost'), ) s.startSearch( 'dc=stroeder,dc=com', ldap.SCOPE_SUBTREE, '(objectClass=*)', ) try: partial = s.processResults() except ldap.SIZELIMIT_EXCEEDED: sys.stderr.write('Warning: Server-side size limit exceeded.\n') else: if partial: sys.stderr.write('Warning: Only partial results received.\n') sys.stdout.write( '%d results received.\n' % ( len(s.allResults) ) ) .. _ldap.async-example.LDIFWriter: Using ldap.async.LDIFWriter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This example demonstrates how to use class ldap.async.LDIFWriter for writing search results as LDIF to stdout. :: import sys,ldap,ldap.async s = ldap.async.LDIFWriter( ldap.initialize('ldap://localhost:1390'), sys.stdout ) s.startSearch( 'dc=stroeder,dc=com', ldap.SCOPE_SUBTREE, '(objectClass=*)', ) try: partial = s.processResults() except ldap.SIZELIMIT_EXCEEDED: sys.stderr.write('Warning: Server-side size limit exceeded.\n') else: if partial: sys.stderr.write('Warning: Only partial results received.\n') sys.stderr.write( '%d results received.\n' % ( s.endResultBreak-s.beginResultsDropped ) ) python-ldap-2.4.10/Doc/ldap-modlist.rst0000644000076400001440000000442511612335224020456 0ustar michaelusers00000000000000.. % $Id: ldap-modlist.rst,v 1.4 2011/07/22 17:39:44 stroeder Exp $ :py:mod:`ldap.modlist` Generate modify lists ============================================== .. py:module:: ldap.modlist The :mod:`ldap.modlist` module defines the following functions: .. function:: addModlist(entry [, ignore_attr_types=[]]) -> list This function builds a list suitable for passing it directly as argument *modlist* to method :py:meth:`ldap.ldapobject.LDAPObject.add` or its synchronous counterpart :py:meth:`ldap.ldapobject.LDAPObject.add_s`. *entry* is a dictionary like returned when receiving search results. *ignore_attr_types* is a list of attribute type names which shall be ignored completely. Attributes of these types will not appear in the result at all. .. function:: modifyModlist( old_entry, new_entry [, ignore_attr_types=[] [, ignore_oldexistent=0 [, case_ignore_attr_types=None]]]) -> list This function builds a list suitable for passing it directly as argument *modlist* to method :py:meth:`ldap.ldapobject.LDAPObject.modify` or its synchronous counterpart :py:meth:`ldap.ldapobject.LDAPObject.modify_s`. Roughly when applying the resulting modify list to an entry holding the data *old_entry* it will be modified in such a way that the entry holds *new_entry* after the modify operation. It is handy in situations when it is impossible to track user changes to an entry's data or for synchronizing operations. *old_entry* and *new_entry* are dictionaries like returned when receiving search results. *ignore_attr_types* is a list of attribute type names which shall be ignored completely. These attribute types will not appear in the result at all. If *ignore_oldexistent* is non-zero attribute type names which are in *old_entry* but are not found in *new_entry* at all are not deleted. This is handy for situations where your application sets attribute value to an empty string for deleting an attribute. In most cases leave zero. If *case_ignore_attr_types* is a list of attribute type names for which the comparison will be conducted case-insensitive. It is useful in situations where a LDAP server normalizes values and one wants to avoid unnecessary changes (e.g. case of attribute type names in DNs). python-ldap-2.4.10/Doc/ldap-schema.rst0000644000076400001440000000370111612335224020237 0ustar michaelusers00000000000000.. % $Id: ldap-schema.rst,v 1.4 2011/07/22 17:46:07 stroeder Exp $ ******************************************** :py:mod:`ldap.schema` Handling LDAPv3 schema ******************************************** .. py:module:: ldap.schema This module deals with schema information usually retrieved from a special subschema subentry provided by the server. It is closely modeled along the directory information model described in the following RFC with which you should make yourself familiar when trying to use this module: .. seealso:: :rfc:`4512` - Lightweight Directory Access Protocol (LDAP): Directory Information Models :py:mod:`ldap.schema.subentry` Processing LDAPv3 subschema subentry ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. py:module:: ldap.schema.subentry .. py:data:: NOT_HUMAN_READABLE_LDAP_SYNTAXES Dictionary where the keys are the OIDs of LDAP syntaxes known to be not human-readable when displayed to a console without conversion and which cannot be decoded to a :py:data:`types.UnicodeType`. Functions ========= .. autofunction:: ldap.schema.subentry.urlfetch Classes ======= .. autoclass:: ldap.schema.subentry.SubSchema :members: :py:mod:`ldap.schema.models` Schema elements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. py:module:: ldap.schema.models .. autoclass:: ldap.schema.models.Entry :members: .. autoclass:: ldap.schema.models.SchemaElement :members: .. autoclass:: ldap.schema.models.AttributeType :members: .. autoclass:: ldap.schema.models.ObjectClass :members: .. autoclass:: ldap.schema.models.MatchingRule :members: .. autoclass:: ldap.schema.models.MatchingRuleUse :members: .. autoclass:: ldap.schema.models.DITContentRule :members: .. autoclass:: ldap.schema.models.NameForm :members: .. autoclass:: ldap.schema.models.DITStructureRule :members: .. _ldap.schema-example: Examples for ldap.schema ^^^^^^^^^^^^^^^^^^^^^^^^ :: import ldap.schema python-ldap-2.4.10/Doc/ldap-filter.rst0000644000076400001440000000244411612335224020267 0ustar michaelusers00000000000000.. % $Id: ldap-filter.rst,v 1.4 2011/07/21 20:33:26 stroeder Exp $ :py:mod:`ldap.filter` LDAP filter handling ============================================ .. py:module:: ldap.filter :synopsis: LDAP filter handling. .. moduleauthor:: python-ldap project (see http://www.python-ldap.org/) .. % Author of the module code; .. seealso:: :rfc:`4515` - Lightweight Directory Access Protocol (LDAP): String Representation of Search Filters. The :mod:`ldap.filter` module defines the following functions: .. function:: escape_filter_chars(assertion_value[, escape_mode=0]) This function escapes characters in *assertion_value* which are special in LDAP filters. You should use this function when building LDAP filter strings from arbitrary input. *escape_mode* means: If :const:`0` only special chars mentioned in RFC 4515 are escaped. If :const:`1` all NON-ASCII chars are escaped. If :const:`2` all chars are escaped. .. % -> string .. function:: filter_format(filter_template, assertion_values) This function applies :func:`escape_filter_chars` to each of the strings in list *assertion_values*. After that *filter_template* containing as many :const:`%s` placeholders as count of assertion values is used to build the whole filter string. .. % -> string python-ldap-2.4.10/Doc/index.rst0000644000076400001440000000151111706071063017167 0ustar michaelusers00000000000000########################## python-ldap Documentation ########################## .. % $Id: index.rst,v 1.8 2011/10/26 19:42:45 stroeder Exp $ .. topic:: Abstract This document describes the package python-ldap with its various modules. Depending on what you want to do this manual assumes basic to expert knowledge about the Python language and the LDAP standard (LDAPv3). ******** Contents ******** .. toctree:: :maxdepth: 3 installing.rst ldap.rst ldap-async.rst ldap-controls.rst ldap-dn.rst ldap-extop.rst ldap-filter.rst ldap-modlist.rst ldap-resiter.rst ldap-schema.rst ldap-syncrepl.rst ldif.rst ldapurl.rst dsml.rst ****************** Indices and tables ****************** * :ref:`genindex` * :ref:`modindex` * :ref:`search` python-ldap-2.4.10/Doc/ldap.rst0000644000076400001440000011226511706070372017013 0ustar michaelusers00000000000000.. % $Id: ldap.rst,v 1.27 2012/01/10 23:28:08 stroeder Exp $ ******************************************** :py:mod:`ldap` LDAP library interface module ******************************************** .. py:module:: ldap :platform: Posix, Windows :synopsis: Access to an underlying LDAP C library. .. moduleauthor:: python-ldap project (see http://www.python-ldap.org/) This module provides access to the LDAP (Lightweight Directory Access Protocol) C API implemented in OpenLDAP 2.3 or newer. It is similar to the C API, with the notable differences that lists are manipulated via Python list operations and errors appear as exceptions. For far more detailed information on the C interface, please see the (expired) draft-ietf-ldapext-ldap-c-api-04. This documentation is current for the Python LDAP module, version |release|. Source and binaries are available from http://www.python-ldap.org/. Functions ========= This module defines the following functions: .. py:function:: initialize(uri [, trace_level=0 [, trace_file=sys.stdout [, trace_stack_limit=None]]]) -> LDAPObject object Initializes a new connection object for accessing the given LDAP server, and return an LDAP object (see :ref:`ldap-objects`) used to perform operations on that server. Parameter *uri* has to be a valid LDAP URL. Note that the C wrapper function :py:func:_ldap.initialize() is called which calls the OpenLDAP funtion ldap_initialize(). Calling this function just initializes the LDAP connection struct in the C API - nothing else. The first call to an operation method (bind, search etc.) then really opens the connection. Before that nothing is sent on the wire. The optional arguments are for generating debug log information: *trace_level* specifies the amount of information being logged, *trace_file* specifies a file-like object as target of the debug log and *trace_stack_limit* specifies the stack limit of tracebacks in debug log. Possible values for *trace_level* are :py:const:`0` for no logging, :py:const:`1` for only logging the method calls with arguments, :py:const:`2` for logging the method calls with arguments and the complete results and :py:const:`9` for also logging the traceback of method calls. .. seealso:: :rfc:`4516` - Lightweight Directory Access Protocol (LDAP): Uniform Resource Locator .. py:function:: open(host [, port=PORT]) -> LDAPObject object Opens a new connection with an LDAP server, and return an LDAP object (see :ref:`ldap-objects`) used to perform operations on that server. *host* is a string containing solely the host name. *port* is an integer specifying the port where the LDAP server is listening (default is 389). Note: Using this function is deprecated. .. py:function:: get_option(option) -> int|string This function returns the value of the global option specified by *option*. .. py:function:: set_option(option, invalue) -> None This function sets the value of the global option specified by *option* to *invalue*. .. _ldap-constants: Constants ========= The module defines various constants. Note that some constants depend on the build options and which underlying libs were used or even on the version of the libs. So before using those constants the application has to explicitly check whether they are available. General ------- .. py:data:: PORT The assigned TCP port number (389) that LDAP servers listen on. .. py:data:: SASL_AVAIL Integer where a non-zero value indicates that python-ldap was built with support for SASL (Cyrus-SASL). .. py:data:: TLS_AVAIL Integer where a non-zero value indicates that python-ldap was built with support for SSL/TLS (OpenSSL or similar libs). .. _ldap-options: Options ------- .. seealso:: :manpage:`ldap.conf{5}` and :manpage:`ldap_get_options{3}` For use with functions :py:func:set_option() and :py:func:get_option() and methods :py:method:LDAPObject.set_option() and :py:method:LDAPObject.get_option() the following option identifiers are defined as constants: .. py:data:: OPT_API_FEATURE_INFO .. py:data:: OPT_API_INFO .. py:data:: OPT_CLIENT_CONTROLS .. py:data:: OPT_DEBUG_LEVEL Sets the debug level within the underlying LDAP C lib. .. py:data:: OPT_DEFBASE .. py:data:: OPT_DEREF Specifies how alias derefencing is done within the underlying LDAP C lib. .. py:data:: OPT_ERROR_STRING .. py:data:: OPT_DIAGNOSTIC_MESSAGE .. py:data:: OPT_HOST_NAME .. py:data:: OPT_MATCHED_DN .. py:data:: OPT_NETWORK_TIMEOUT .. py:data:: OPT_PROTOCOL_VERSION Sets the LDAP protocol version used for a connection. This is mapped to object attribute `ldap.LDAPObject.protocol_version` .. py:data:: OPT_REFERRALS int specifying whether referrals should be automatically chased within the underlying LDAP C lib. .. py:data:: OPT_REFHOPLIMIT .. py:data:: OPT_RESTART .. py:data:: OPT_SERVER_CONTROLS .. py:data:: OPT_SIZELIMIT .. py:data:: OPT_SUCCESS .. py:data:: OPT_TIMELIMIT .. py:data:: OPT_TIMEOUT .. py:data:: OPT_URI .. _ldap-sasl-options: SASL options :::::::::::: .. py:data:: OPT_X_SASL_AUTHCID .. py:data:: OPT_X_SASL_AUTHZID .. py:data:: OPT_X_SASL_MECH .. py:data:: OPT_X_SASL_NOCANON If set to zero SASL host name canonicalization is disabled. .. py:data:: OPT_X_SASL_REALM .. py:data:: OPT_X_SASL_SECPROPS .. py:data:: OPT_X_SASL_SSF .. py:data:: OPT_X_SASL_SSF_EXTERNAL .. py:data:: OPT_X_SASL_SSF_MAX .. py:data:: OPT_X_SASL_SSF_MIN .. _ldap-tls-options: TLS options ::::::::::: .. py:data:: OPT_X_TLS .. py:data:: OPT_X_TLS_ALLOW .. py:data:: OPT_X_TLS_CACERTDIR .. py:data:: OPT_X_TLS_CACERTFILE .. py:data:: OPT_X_TLS_CERTFILE .. py:data:: OPT_X_TLS_CIPHER_SUITE .. py:data:: OPT_X_TLS_CTX .. py:data:: OPT_X_TLS_DEMAND .. py:data:: OPT_X_TLS_HARD .. py:data:: OPT_X_TLS_KEYFILE .. py:data:: OPT_X_TLS_NEVER .. py:data:: OPT_X_TLS_RANDOM_FILE .. py:data:: OPT_X_TLS_REQUIRE_CERT .. py:data:: OPT_X_TLS_TRY .. _ldap-keepalive-options: Keepalive options ::::::::::::::::: .. py:data:: OPT_X_KEEPALIVE_IDLE .. py:data:: OPT_X_KEEPALIVE_PROBES .. py:data:: OPT_X_KEEPALIVE_INTERVAL .. _ldap-dn-flags: DN format flags ---------------- This constants are used for DN-parsing functions found in sub-module :py:mod:`ldap.dn`. .. seealso:: :manpage:`ldap_str2dn{3}` .. py:data:: DN_FORMAT_LDAP .. py:data:: DN_FORMAT_LDAPV3 .. py:data:: DN_FORMAT_LDAPV2 .. py:data:: DN_FORMAT_DCE .. py:data:: DN_FORMAT_UFN .. py:data:: DN_FORMAT_AD_CANONICAL .. py:data:: DN_FORMAT_MASK .. py:data:: DN_PRETTY .. py:data:: DN_SKIP .. py:data:: DN_P_NOLEADTRAILSPACES .. py:data:: DN_P_NOSPACEAFTERRDN .. py:data:: DN_PEDANTIC .. _ldap-exceptions: Exceptions ========== The module defines the following exceptions: .. py:exception:: LDAPError This is the base class of all execeptions raised by the module :py:mod:`ldap`. Unlike the C interface, errors are not returned as result codes, but are instead turned into exceptions, raised as soon an the error condition is detected. The exceptions are accompanied by a dictionary possibly containing an string value for the key :py:const:`desc` (giving an English description of the error class) and/or a string value for the key :py:const:`info` (giving a string containing more information that the server may have sent). A third possible field of this dictionary is :py:const:`matched` and is set to a truncated form of the name provided or alias dereferenced for the lowest entry (object or alias) that was matched. .. py:exception:: ADMINLIMIT_EXCEEDED .. py:exception:: AFFECTS_MULTIPLE_DSAS .. py:exception:: ALIAS_DEREF_PROBLEM A problem was encountered when dereferencing an alias. (Sets the :py:const:`matched` field.) .. py:exception:: ALIAS_PROBLEM An alias in the directory points to a nonexistent entry. (Sets the :py:const:`matched` field.) .. py:exception:: ALREADY_EXISTS The entry already exists. E.g. the *dn* specified with :py:meth:`add()` already exists in the DIT. .. py:exception:: AUTH_UNKNOWN The authentication method specified to :py:meth:`bind()` is not known. .. py:exception:: BUSY The DSA is busy. .. py:exception:: CLIENT_LOOP .. py:exception:: COMPARE_FALSE A compare operation returned false. (This exception should never be seen because :py:meth:`compare()` returns a boolean result.) .. py:exception:: COMPARE_TRUE A compare operation returned true. (This exception should never be seen because :py:meth:`compare()` returns a boolean result.) .. py:exception:: CONFIDENTIALITY_REQUIRED Indicates that the session is not protected by a protocol such as Transport Layer Security (TLS), which provides session confidentiality. .. py:exception:: CONNECT_ERROR .. py:exception:: CONSTRAINT_VIOLATION An attribute value specified or an operation started violates some server-side constraint (e.g., a postalAddress has too many lines or a line that is too long or a password is expired). .. py:exception:: CONTROL_NOT_FOUND .. py:exception:: DECODING_ERROR An error was encountered decoding a result from the LDAP server. .. py:exception:: ENCODING_ERROR An error was encountered encoding parameters to send to the LDAP server. .. py:exception:: FILTER_ERROR An invalid filter was supplied to :py:meth:`search()` (e.g. unbalanced parentheses). .. py:exception:: INAPPROPRIATE_AUTH Inappropriate authentication was specified (e.g. :py:const:`AUTH_SIMPLE` was specified and the entry does not have a userPassword attribute). .. py:exception:: INAPPROPRIATE_MATCHING Filter type not supported for the specified attribute. .. py:exception:: INSUFFICIENT_ACCESS The user has insufficient access to perform the operation. .. py:exception:: INVALID_CREDENTIALS Invalid credentials were presented during :py:meth:`bind()` or :py:meth:`simple_bind()`. (e.g., the wrong password). .. py:exception:: INVALID_DN_SYNTAX A syntactically invalid DN was specified. (Sets the :py:const:`matched` field.) .. py:exception:: INVALID_SYNTAX An attribute value specified by the client did not comply to the syntax defined in the server-side schema. .. py:exception:: IS_LEAF The object specified is a leaf of the diretcory tree. Sets the :py:const:`matched` field of the exception dictionary value. .. py:exception:: LOCAL_ERROR Some local error occurred. This is usually due to failed memory allocation. .. py:exception:: LOOP_DETECT A loop was detected. .. py:exception:: MORE_RESULTS_TO_RETURN .. py:exception:: NAMING_VIOLATION A naming violation occurred. This is raised e.g. if the LDAP server has constraints about the tree naming. .. py:exception:: NO_OBJECT_CLASS_MODS Modifying the objectClass attribute as requested is not allowed (e.g. modifying structural object class of existing entry). .. py:exception:: NOT_ALLOWED_ON_NONLEAF The operation is not allowed on a non-leaf object. .. py:exception:: NOT_ALLOWED_ON_RDN The operation is not allowed on an RDN. .. py:exception:: NOT_SUPPORTED .. py:exception:: NO_MEMORY .. py:exception:: NO_OBJECT_CLASS_MODS Object class modifications are not allowed. .. py:exception:: NO_RESULTS_RETURNED .. py:exception:: NO_SUCH_ATTRIBUTE The attribute type specified does not exist in the entry. .. py:exception:: NO_SUCH_OBJECT The specified object does not exist in the directory. Sets the :py:const:`matched` field of the exception dictionary value. .. py:exception:: OBJECT_CLASS_VIOLATION An object class violation occurred when the LDAP server checked the data sent by the client against the server-side schema (e.g. a "must" attribute was missing in the entry data). .. py:exception:: OPERATIONS_ERROR An operations error occurred. .. py:exception:: OTHER An unclassified error occurred. .. py:exception:: PARAM_ERROR An ldap routine was called with a bad parameter. .. py:exception:: PARTIAL_RESULTS Partial results only returned. This exception is raised if a referral is received when using LDAPv2. (This exception should never be seen with LDAPv3.) .. py:exception:: PROTOCOL_ERROR A violation of the LDAP protocol was detected. .. py:exception:: RESULTS_TOO_LARGE The result does not fit into a UDP packet. This happens only when using UDP-based CLDAP (connection-less LDAP) which is not supported anyway. .. py:exception:: SASL_BIND_IN_PROGRESS .. py:exception:: SERVER_DOWN The LDAP library can't contact the LDAP server. .. py:exception:: SIZELIMIT_EXCEEDED An LDAP size limit was exceeded. This could be due to a ``sizelimit`` configuration on the LDAP server. .. py:exception:: STRONG_AUTH_NOT_SUPPORTED The LDAP server does not support strong authentication. .. py:exception:: STRONG_AUTH_REQUIRED Strong authentication is required for the operation. .. py:exception:: TIMELIMIT_EXCEEDED An LDAP time limit was exceeded. .. py:exception:: TIMEOUT A timelimit was exceeded while waiting for a result from the server. .. py:exception:: TYPE_OR_VALUE_EXISTS An attribute type or attribute value specified already exists in the entry. .. py:exception:: UNAVAILABLE The DSA is unavailable. .. py:exception:: UNAVAILABLE_CRITICAL_EXTENSION Indicates that the LDAP server was unable to satisfy a request because one or more critical extensions were not available. Either the server does not support the control or the control is not appropriate for the operation type. .. py:exception:: UNDEFINED_TYPE An attribute type used is not defined in the server-side schema. .. py:exception:: UNWILLING_TO_PERFORM The DSA is unwilling to perform the operation. .. py:exception:: USER_CANCELLED The operation was cancelled via the :py:meth:`abandon()` method. The above exceptions are raised when a result code from an underlying API call does not indicate success. .. _ldap-objects: LDAPObject classes ================== .. py:class:: LDAPObject Instances of :py:class:`LDAPObject` are returned by :py:func:`initialize()` and :py:func:`open()` (deprecated). The connection is automatically unbound and closed when the LDAP object is deleted. Internally :py:class:`LDAPObject` is set to :py:class:`SimpleLDAPObject` by default. .. py:class:: SimpleLDAPObject(uri [, trace_level=0 [, trace_file=sys.stdout [, trace_stack_limit=5]]]) Instances of :py:class:`LDAPObject` are returned by :py:func:`initialize()` and :py:func:`open()` (deprecated). The connection is automatically unbound and closed when the LDAP object is deleted. .. py:class:: ReconnectLDAPObject(uri [, trace_level=0 [, trace_file=sys.stdout [, trace_stack_limit=5] [, retry_max=1 [, retry_delay=60.0]]]]) This class is derived from :py:class:`SimpleLDAPObject` and used for automatic reconnects when using the synchronous request methods (see below). This class also implements the pickle protocol. For automatic reconnects it has additional arguments: *retry_max* specifies the number of reconnect attempts before re-raising the :py:exc:`ldap.SERVER_DOWN` exception. *retry_delay* specifies the time in seconds between reconnect attempts. Arguments for LDAPv3 controls ----------------------------- The :py:mod:`ldap.controls` module can be used for constructing and decoding LDAPv3 controls. These arguments are available in the methods with names ending in :py:const:`_ext` or :py:const:`_ext_s`: *serverctrls* is a list of :py:class:`ldap.controls.LDAPControl` instances sent to the server along with the LDAP request (see module :py:mod:`ldap.controls`). These are controls which alter the behaviour of the server when processing the request if the control is supported by the server. The effect of controls might differ depending on the type of LDAP request or controls might not be applicable with certain LDAP requests at all. *clientctrls* is a list of :py:class:`ldap.controls.LDAPControl` instances passed to the client API and alter the behaviour of the client when processing the request. Sending LDAP requests --------------------- Most methods on LDAP objects initiate an asynchronous request to the LDAP server and return a message id that can be used later to retrieve the result with :py:meth:`result()`. Methods with names ending in :py:const:`_s` are the synchronous form and wait for and return with the server's result, or with :py:const:`None` if no data is expected. :class:`LDAPObject` instances have the following methods: .. py:method:: LDAPObject.abandon(msgid) -> None .. py:method:: LDAPObject.abandon_ext(msgid [, serverctrls=None [, clientctrls=None]]) -> None Abandons an LDAP operation in progress without waiting for a LDAP response. The *msgid* argument should be the message ID of an outstanding LDAP operation as returned by the asynchronous methods :py:meth:`search()`, :py:meth:`modify()`, etc. The caller can expect that the result of an abandoned operation will not be returned from a future call to :py:meth:`result()`. *serverctrls* and *clientctrls* like described above. .. py:method:: LDAPObject.add(dn, modlist) -> int .. py:method:: LDAPObject.add_s(dn, modlist) -> None .. py:method:: LDAPObject.add_ext(dn, modlist [, serverctrls=None [, clientctrls=None]]) -> int .. py:method:: LDAPObject.add_ext_s(dn, modlist [, serverctrls=None [, clientctrls=None]]) -> None Performs an LDAP add operation. The *dn* argument is the distinguished name (DN) of the entry to add, and *modlist* is a list of attributes to be added. The modlist is similar the one passed to :py:meth:`modify()`, except that the operation integer is omitted from the tuples in modlist. You might want to look into sub-module \refmodule{ldap.modlist} for generating the modlist. The asynchronous methods :py:meth:`add()` and :py:meth:`add_ext()` return the message ID of the initiated request. *serverctrls* and *clientctrls* like described above. .. py:method:: LDAPObject.bind(who, cred, method) -> int .. py:method:: LDAPObject.bind_s(who, cred, method) -> None .. py:method:: LDAPObject.simple_bind([who='' [, cred='']]) -> int .. py:method:: LDAPObject.simple_bind_s([who='' [, cred='']]) -> None After an LDAP object is created, and before any other operations can be attempted over the connection, a bind operation must be performed. This method attempts to bind with the LDAP server using either simple authentication, or Kerberos (if available). The first and most general method, :py:meth:`bind()`, takes a third parameter, *method* which can currently solely be :py:const:`AUTH_SIMPLE`. .. py:method:: LDAPObject.sasl_interactive_bind_s(who, auth) -> None This call is used to bind to the directory with a SASL bind request. .. py:method:: LDAPObject.cancel( cancelid, [, serverctrls=None [, clientctrls=None]]) -> None Send cancels extended operation for an LDAP operation specified by *cancelid*. The *cancelid* should be the message id of an outstanding LDAP operation as returned by the asynchronous methods search(), modify() etc. The caller can expect that the result of an abandoned operation will not be returned from a future call to :py:meth:`result()`. In opposite to :py:meth:`abandon()` this extended operation gets an result from the server and thus should be preferred if the server supports it. *serverctrls* and *clientctrls* like described above. :rfc:`3909` - Lightweight Directory Access Protocol (LDAP): Cancel Operation .. py:method:: LDAPObject.compare(dn, attr, value) -> int .. py:method:: LDAPObject.compare_s(dn, attr, value) -> tuple .. py:method:: LDAPObject.compare_ext(dn, attr, value [, serverctrls=None [, clientctrls=None]]) -> int .. py:method:: LDAPObject.compare_ext_s(dn, attr, value [, serverctrls=None [, clientctrls=None]]) -> tuple Perform an LDAP comparison between the attribute named *attr* of entry *dn*, and the value *value*. The synchronous forms returns :py:const:`0` for false, or :py:const:`1` for true. The asynchronous forms returns the message ID of the initiated request, and the result of the asynchronous compare can be obtained using :py:meth:`result()`. Note that the asynchronous technique yields the answer by raising the exception objects :py:exc:`ldap.COMPARE_TRUE` or :py:exc:`ldap.COMPARE_FALSE`. *serverctrls* and *clientctrls* like described above. .. note:: A design fault in the LDAP API prevents *value* from containing *NULL* characters. .. py:method:: LDAPObject.delete(dn) -> int .. py:method:: LDAPObject.delete_s(dn) -> None .. py:method:: LDAPObject.delete_ext(dn [, serverctrls=None [, clientctrls=None]]) -> int .. py:method:: LDAPObject.delete_ext_s(dn [, serverctrls=None [, clientctrls=None]]) -> None Performs an LDAP delete operation on *dn*. The asynchronous form returns the message id of the initiated request, and the result can be obtained from a subsequent call to :py:meth:`result()`. *serverctrls* and *clientctrls* like described above. .. py:method:: extop(extreq[,serverctrls=None[,clientctrls=None]]]) -> int .. py:method:: extop_s(extreq[,serverctrls=None[,clientctrls=None[,extop_resp_class=None]]]]) -> (respoid,respvalue) Performs an LDAP extended operation. The asynchronous form returns the message id of the initiated request, and the result can be obtained from a subsequent call to :py:meth:`extop_result()`. The *extreq* is an instance of class :py:class:`ldap.extop.ExtendedRequest` containing the parameters for the extended operation request. If argument *extop_resp_class* is set to a sub-class of :py:class:`ldap.extop.ExtendedResponse` this class is used to return an object of this class instead of a raw BER value in respvalue. .. py:method:: extop_result(self,msgid=ldap.RES_ANY,all=1,timeout=None) -> (respoid,respvalue) Wrapper method around :py:meth:`result4()` just for retrieving the result of an extended operation sent before. .. py:method:: LDAPObject.modify(dn, modlist) -> int .. py:method:: LDAPObject.modify_s(dn, modlist) -> None .. py:method:: LDAPObject.modify_ext(dn, modlist [, serverctrls=None [, clientctrls=None]]) -> int .. py:method:: LDAPObject.modify_ext_s(dn, modlist [, serverctrls=None [, clientctrls=None]]) -> None Performs an LDAP modify operation on an entry's attributes. The *dn* argument is the distinguished name (DN) of the entry to modify, and *modlist* is a list of modifications to make to that entry. Each element in the list *modlist* should be a tuple of the form *(mod_op,mod_type,mod_vals)*, where *mod_op* indicates the operation (one of :py:const:`ldap.MOD_ADD`, :py:const:`ldap.MOD_DELETE`, or :py:const:`ldap.MOD_REPLACE`), *mod_type* is a string indicating the attribute type name, and *mod_vals* is either a string value or a list of string values to add, delete or replace respectively. For the delete operation, *mod_vals* may be :py:const:`None` indicating that all attributes are to be deleted. *serverctrls* and *clientctrls* like described above. The asynchronous methods :py:meth:`modify()` and :py:meth:`modify_ext()` return the message ID of the initiated request. You might want to look into sub-module :py:mod:`ldap.modlist` for generating *modlist*. .. py:method:: LDAPObject.modrdn(dn, newrdn [, delold=1]) -> int .. py:method:: LDAPObject.modrdn_s(dn, newrdn [, delold=1]) -> None Perform a ``modify RDN`` operation, (i.e. a renaming operation). These routines take *dn* (the DN of the entry whose RDN is to be changed, and *newrdn*, the new RDN to give to the entry. The optional parameter *delold* is used to specify whether the old RDN should be kept as an attribute of the entry or not. The asynchronous version returns the initiated message id. This operation is emulated by :py:meth:`rename()` and :py:meth:`rename_s()` methods since the modrdn2* routines in the C library are deprecated. .. py:method:: LDAPObject.passwd(user, oldpw, newpw [, serverctrls=None [, clientctrls=None]]) -> int .. py:method:: LDAPObject.passwd_s(user, oldpw, newpw [, serverctrls=None [, clientctrls=None]]) -> None Perform a ``LDAP Password Modify Extended Operation`` operation on the entry specified by *user*. The old password in *oldpw* is replaced with the new password in *newpw* by a LDAP server supporting this operation. If *oldpw* is not :py:const:`None` it has to match the old password of the specified *user* which is sometimes used when a user changes his own password. *serverctrls* and *clientctrls* like described above. The asynchronous version returns the initiated message id. .. seealso:: :rfc:`3062` - LDAP Password Modify Extended Operation .. py:method:: LDAPObject.rename(dn, newrdn [, newsuperior=None [, delold=1 [, serverctrls=None [, clientctrls=None]]]]) -> int .. py:method:: LDAPObject.rename_s(dn, newrdn [, newsuperior=None [, delold=1 [, serverctrls=None [, clientctrls=None]]]]) -> None Perform a ``Rename`` operation, (i.e. a renaming operation). These routines take *dn* (the DN of the entry whose RDN is to be changed, and *newrdn*, the new RDN to give to the entry. The optional parameter *newsuperior* is used to specify a new parent DN for moving an entry in the tree (not all LDAP servers support this). The optional parameter *delold* is used to specify whether the old RDN should be kept as an attribute of the entry or not. *serverctrls* and *clientctrls* like described above. .. py:method:: LDAPObject.result([msgid=RES_ANY [, all=1 [, timeout=None]]]) -> 2-tuple This method is used to wait for and return the result of an operation previously initiated by one of the LDAP *asynchronous* operations (eg :py:meth:`search()`, :py:meth:`modify()`, etc.) The *msgid* parameter is the integer identifier returned by that method. The identifier is guaranteed to be unique across an LDAP session, and tells the :py:meth:`result()` method to request the result of that specific operation. If a result is desired from any one of the in-progress operations, *msgid* should be specified as the constant :py:const:`RES_ANY` and the method :py:meth:`result2()` should be used instead. The *all* parameter only has meaning for :py:meth:`search()` responses and is used to select whether a single entry of the search response should be returned, or to wait for all the results of the search before returning. A search response is made up of zero or more search entries followed by a search result. If *all* is 0, search entries will be returned one at a time as they come in, via separate calls to :py:meth:`result()`. If all is 1, the search response will be returned in its entirety, i.e. after all entries and the final search result have been received. For *all* set to 0, result tuples trickle in (with the same message id), and with the result types :py:const:`RES_SEARCH_ENTRY` and :py:const:`RES_SEARCH_REFERENCE`, until the final result which has a result type of :py:const:`RES_SEARCH_RESULT` and a (usually) empty data field. When all is set to 1, only one result is returned, with a result type of RES_SEARCH_RESULT, and all the result tuples listed in the data field. The *timeout* parameter is a limit on the number of seconds that the method will wait for a response from the server. If *timeout* is negative (which is the default), the method will wait indefinitely for a response. The timeout can be expressed as a floating-point value, and a value of :py:const:`0` effects a poll. If a timeout does occur, a :py:exc:`ldap.TIMEOUT` exception is raised, unless polling, in which case ``(None, None)`` is returned. The :py:meth:`result()` method returns a tuple of the form ``(result-type, result-data)``. The first element, ``result-type`` is a string, being one of these module constants: :py:const:`RES_BIND`, :py:const:`RES_SEARCH_ENTRY`, :py:const:`RES_SEARCH_REFERENCE`, :py:const:`RES_SEARCH_RESULT`, :py:const:`RES_MODIFY`, :py:const:`RES_ADD`, :py:const:`RES_DELETE`, :py:const:`RES_MODRDN`, or :py:const:`RES_COMPARE`. If *all* is :py:const:`0`, one response at a time is returned on each call to :py:meth:`result()`, with termination indicated by ``result-data`` being an empty list. See :py:meth:`search()` for a description of the search result's ``result-data``, otherwise the ``result-data`` is normally meaningless. .. py:method:: LDAPObject.result2([msgid=RES_ANY [, all=1 [, timeout=None]]]) -> 3-tuple This method behaves almost exactly like :py:meth:`result()`. But it returns a 3-tuple also containing the message id of the outstanding LDAP operation a particular result message belongs to. This is especially handy if one needs to dispatch results obtained with ``msgid=``:py:const:`RES_ANY` to several consumer threads which invoked a particular LDAP operation. .. py:method:: LDAPObject.result3([msgid=RES_ANY [, all=1 [, timeout=None]]]) -> 4-tuple This method behaves almost exactly like :py:meth:`result2()`. But it returns an extra item in the tuple, the decoded server controls. .. py:method:: LDAPObject.result4([msgid=RES_ANY [, all=1 [, timeout=None [, add_ctrls=0 [, add_intermediates=0 [, add_extop=0 [, resp_ctrl_classes=None]]]]]]]) -> 6-tuple This method behaves almost exactly like :py:meth:`result3()`. But it returns an extra items in the tuple, the decoded results of an extended response. The additional arguments are: *add_ctrls* (integer flag) specifies whether response controls are returned. add_intermediates (integer flag) specifies whether response controls of intermediate search results are returned. *add_extop* (integer flag) specifies whether the response of an extended operation is returned. If using extended operations you should consider using the method :py:meth:`extop_result()` or :py:meth:`extop_s()` instead. *resp_ctrl_classes* is a dictionary mapping the OID of a response controls to a :py:class:`ldap.controls.ResponseControl` class of response controls known by the application. So the response control value will be automatically decoded. If :py:const:`None` the global dictionary :py:data:`ldap.controls.KNOWN_RESPONSE_CONTROLS` is used instead. .. py:method:: LDAPObject.search(base, scope [,filterstr='(objectClass=*)' [, attrlist=None [, attrsonly=0]]]) ->int .. py:method:: LDAPObject.search_s(base, scope [,filterstr='(objectClass=*)' [, attrlist=None [, attrsonly=0]]]) ->list|None .. py:method:: LDAPObject.search_st(base, scope [,filterstr='(objectClass=*)' [, attrlist=None [, attrsonly=0 [, timeout=-1]]]]) -> list|None .. py:method:: LDAPObject.search_ext(base, scope [,filterstr='(objectClass=*)' [, attrlist=None [, attrsonly=0 [, serverctrls=None [, clientctrls=None [, timeout=-1 [, sizelimit=0]]]]]]]) -> int .. py:method:: LDAPObject.search_ext_s(base, scope [,filterstr='(objectClass=*)' [, attrlist=None [, attrsonly=0 [, serverctrls=None [, clientctrls=None [, timeout=-1 [, sizelimit=0]]]]]]]) -> list|None Perform an LDAP search operation, with *base* as the DN of the entry at which to start the search, *scope* being one of :py:const:`SCOPE_BASE` (to search the object itself), :py:const:`SCOPE_ONELEVEL` (to search the object's immediate children), or :py:const:`SCOPE_SUBTREE` (to search the object and all its descendants). The *filterstr* argument is a string representation of the filter to apply in the search. .. seealso:: :rfc:`4515` - Lightweight Directory Access Protocol (LDAP): String Representation of Search Filters. Each result tuple is of the form ``(dn, attrs)``, where *dn* is a string containing the DN (distinguished name) of the entry, and *attrs* is a dictionary containing the attributes associated with the entry. The keys of *attrs* are strings, and the associated values are lists of strings. The DN in *dn* is automatically extracted using the underlying libldap function :cfunc:`ldap_get_dn()`, which may raise an exception if the DN is malformed. If *attrsonly* is non-zero, the values of *attrs* will be meaningless (they are not transmitted in the result). The retrieved attributes can be limited with the *attrlist* parameter. If *attrlist* is :py:const:`None`, all the attributes of each entry are returned. *serverctrls* and *clientctrls* like described above. The synchronous form with timeout, :py:meth:`search_st()` or :py:meth:`search_ext_s()`, will block for at most *timeout* seconds (or indefinitely if *timeout* is negative). A :py:exc:`ldap.TIMEOUT` exception is raised if no result is received within the specified time. The amount of search results retrieved can be limited with the *sizelimit* parameter when using :py:meth:`search_ext()` or :py:meth:`search_ext_s()` (client-side search limit). If non-zero not more than *sizelimit* results are returned by the server. .. py:method:: LDAPObject.start_tls_s() -> None Negotiate TLS with server. The ``version`` attribute must have been set to :py:const:`VERSION3` (which it is by default) before calling this method. If TLS could not be started an exception will be raised. .. seealso:: :rfc:`2830` - Lightweight Directory Access Protocol (v3): Extension for Transport Layer Security .. py:method:: LDAPObject.unbind() -> int .. py:method:: LDAPObject.unbind_s() -> None .. py:method:: LDAPObject.unbind_ext([, serverctrls=None [, clientctrls=None]]) -> int .. py:method:: LDAPObject.unbind_ext_s([, serverctrls=None [, clientctrls=None]]) -> None This call is used to unbind from the directory, terminate the current association, and free resources. Once called, the connection to the LDAP server is closed and the LDAP object is marked invalid. Further invocation of methods on the object will yield exceptions. *serverctrls* and *clientctrls* like described above. These methods are all synchronous in nature. .. py:method:: LDAPObject.whoami_s() -> string This synchronous method implements the LDAP "Who Am I?" extended operation. It is useful for finding out to find out which identity is assumed by the LDAP server after a SASL bind. .. seealso:: :rfc:`4532` - Lightweight Directory Access Protocol (LDAP) "Who am I?" Operation Connection-specific LDAP options -------------------------------- .. py:method:: LDAPObject.get_option(option) -> int|string This method returns the value of the LDAPObject option specified by *option*. .. py:method:: LDAPObject.set_option(option, invalue) -> None This method sets the value of the LDAPObject option specified by *option* to *invalue*. Object attributes ----------------- If the underlying library provides enough information, each LDAP object will also have the following attributes. These attributes are mutable unless described as read-only. .. py:attribute:: LDAPObject.deref -> int Controls whether aliases are automatically dereferenced. This must be one of :py:const:`DEREF_NEVER`, :py:const:`DEREF_SEARCHING`, :py:const:`DEREF_FINDING` or :py:const:`DEREF_ALWAYS`. This option is mapped to option constant :py:const:`OPT_DEREF` and used in the underlying OpenLDAP client lib. .. py:attribute:: LDAPObject.network_timeout -> int Limit on waiting for a network response, in seconds. Defaults to :py:const:`NO_LIMIT`. This option is mapped to option constant :py:const:`OPT_NETWORK_TIMEOUT` and used in the underlying OpenLDAP client lib. .. py:attribute:: LDAPObject.protocol_version -> int Version of LDAP in use (either :py:const:`VERSION2` for LDAPv2 or :py:const:`VERSION3` for LDAPv3). This option is mapped to option constant :py:const:`OPT_PROTOCOL_VERSION` and used in the underlying OpenLDAP client lib. .. note:: It is highly recommended to set the protocol version after establishing a LDAP connection with :py:func:`ldap.initialize()` and before submitting the first request. .. py:attribute:: LDAPObject.sizelimit -> int Limit on size of message to receive from server. Defaults to :py:const:`NO_LIMIT`. This option is mapped to option constant :py:const:`OPT_SIZELIMIT` and used in the underlying OpenLDAP client lib. Its use is deprecated in favour of *sizelimit* parameter when using :py:meth:`search_ext()`. .. py:attribute:: LDAPObject.timelimit -> int Limit on waiting for any response, in seconds. Defaults to :py:const:`NO_LIMIT`. This option is mapped to option constant :py:const:`OPT_TIMELIMIT` and used in the underlying OpenLDAP client lib. Its use is deprecated in favour of using :py:attr:`timeout`. .. py:attribute:: LDAPObject.timeout -> int Limit on waiting for any response, in seconds. Defaults to :py:const:`NO_LIMIT`. This option is used in the wrapper module. .. _ldap-example: Example ======= The following example demonstrates how to open a connection to an LDAP server using the :py:mod:`ldap` module and invoke a synchronous subtree search. >>> import ldap >>> l = ldap.initialize('ldap://localhost:1390') >>> l.search_s('ou=Testing,dc=stroeder,dc=de',ldap.SCOPE_SUBTREE,'(cn=fred*)',['cn','mail']) [('cn=Fred Feuerstein,ou=Testing,dc=stroeder,dc=de', {'cn': ['Fred Feuerstein']})] >>> r = l.search_s('ou=Testing,dc=stroeder,dc=de',ldap.SCOPE_SUBTREE,'(objectClass=*)',['cn','mail']) >>> for dn,entry in r: >>> print 'Processing',repr(dn) >>> handle_ldap_entry(entry) python-ldap-2.4.10/Doc/installing.rst0000644000076400001440000000553211643040601020225 0ustar michaelusers00000000000000.. % $Id: installing.rst,v 1.15 2011/07/24 19:00:53 stroeder Exp $ *********************** Building and installing *********************** python-ldap is built and installed using the Python DistUtils installed along with your Python installation: :: python setup.py build python setup.py install If you have more than one Python interpreter installed locally you should use the same one you plan to use python-ldap with. See further instructions for using DistUtils here: http://docs.python.org/install/index.html Prerequisites ============= The following software packages are required to be installed on the local system when building python-ldap: - Python version 2.3 or later including its development files: http://www.python.org/ - OpenLDAP client libs version 2.4.11 or later: http://www.openldap.org/ It is not possible and not supported to build with prior versions. - OpenSSL (optional): http://www.openssl.org/ - cyrus-sasl (optional): http://www.cyrussasl.org - Kerberos libs, MIT or heimdal (optional) Furthermore some sub-modules of :py:mod:`ldap.controls` and :py:mod:`ldap.extop` require :py:mod:`pyasn1` and :py:mod:`pyasn1_modules` to be installed. http://pyasn1.sf.net setup.cfg ========= The file setup.cfg allows to set some build and installation parameters for reflecting the local installation of required software packages. Only section [_ldap] is described here. More information about other sections can be found in the documentation of Python's DistUtils. .. data:: library_dirs Specifies in which directories to search for required libraries. .. data:: include_dirs Specifies in which directories to search for include files of required libraries. .. data:: libs A space-separated list of library names to link to (see :ref:`libs-used-label`). .. data:: extra_compile_args Compiler options. .. data:: extra_objects .. _libs-used-label: Libs used --------- .. data:: ldap .. data:: ldap_r The LDAP protocol library of OpenLDAP. ldap_r is the reentrant version and should be preferred. .. data:: lber The BER encoder/decoder library of OpenLDAP. .. data:: sasl2 The Cyrus-SASL library if needed and present during build .. data:: ssl The SSL/TLS library of OpenSSL if needed and present during build .. data:: crypto The basic cryptographic library of OpenSSL if needed and present during build Example ============= The following example is for a full-featured build (including SSL and SASL support) of python-ldap with OpenLDAP installed in a different prefix directory (here /opt/openldap-2.3) and SASL header files found in /usr/include/sasl. Debugging symbols are preserved with compile option -g. :: [_ldap] library_dirs = /opt/openldap-2.3/lib include_dirs = /opt/openldap-2.3/include /usr/include/sasl extra_compile_args = -g extra_objects = libs = ldap_r lber sasl2 ssl crypto python-ldap-2.4.10/Doc/ldap-extop.rst0000644000076400001440000000211311706071063020134 0ustar michaelusers00000000000000.. % $Id: ldap-extop.rst,v 1.4 2011/07/22 17:28:46 stroeder Exp $ ******************************************************************** :py:mod:`ldap.extop` High-level access to LDAPv3 extended operations ******************************************************************** .. py:module:: ldap.extop :synopsis: High-level access to LDAPv3 extended operations. Classes ======= This module defines the following classes: .. autoclass:: ldap.extop.ExtendedRequest :members: .. autoclass:: ldap.extop.ExtendedResponse :members: :py:mod:`ldap.extop.dds` Classes for Dynamic Entries extended operations ======================================================================== .. py:module:: ldap.extop.dds :synopsis: Classes for Dynamic Entries extended operations This requires :py:mod:`pyasn1` and :py:mod:`pyasn1_modules` to be installed. .. seealso:: :rfc:`2589` - Lightweight Directory Access Protocol (v3): Extensions for Dynamic Directory Services .. autoclass:: ldap.extop.dds.RefreshRequest :members: .. autoclass:: ldap.extop.dds.RefreshResponse :members: