python-ldap-2.4.22/0000755000175000001440000000000012612730505014622 5ustar michaelusers00000000000000python-ldap-2.4.22/setup.cfg0000644000175000001440000000107412612730505016445 0ustar michaelusers00000000000000[_ldap] library_dirs = /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 include_dirs = /usr/include /usr/include/sasl /usr/local/include /usr/local/include/sasl 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.22/Modules/0000755000175000001440000000000012612730505016232 5ustar michaelusers00000000000000python-ldap-2.4.22/Modules/functions.h0000644000175000001440000000047711573747405020437 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.22/Modules/schema.c0000644000175000001440000002027611573747405017661 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.22/Modules/constants.h0000644000175000001440000000111011573747405020424 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.22/Modules/ldapcontrol.h0000644000175000001440000000065011573747405020741 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.22/Modules/LDAPObject.c0000644000175000001440000012044312534412774020261 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: LDAPObject.c,v 1.91 2015/05/02 16:19:23 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_bind_s( LDAPObject* self, PyObject* args ) { const char *dn; const char *mechanism; struct berval cred; Py_ssize_t cred_len; PyObject *serverctrls = Py_None; PyObject *clientctrls = Py_None; LDAPControl** server_ldcs = NULL; LDAPControl** client_ldcs = NULL; struct berval *servercred; int ldaperror; if (!PyArg_ParseTuple(args, "zzz#OO", &dn, &mechanism, &cred.bv_val, &cred_len, &serverctrls, &clientctrls )) return NULL; if (not_valid(self)) return NULL; cred.bv_len = cred_len; 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_s(self->ldap, dn, mechanism, cred.bv_val ? &cred : NULL, (LDAPControl**) server_ldcs, (LDAPControl**) client_ldcs, &servercred); LDAP_END_ALLOW_THREADS( self ); LDAPControl_List_DEL( server_ldcs ); LDAPControl_List_DEL( client_ldcs ); if (ldaperror == LDAP_SASL_BIND_IN_PROGRESS) { if (servercred && servercred->bv_val && *servercred->bv_val) return PyString_FromStringAndSize( servercred->bv_val, servercred->bv_len ); } else if (ldaperror != LDAP_SUCCESS) return LDAPerror( self->ldap, "l_ldap_sasl_bind_s" ); return PyInt_FromLong( ldaperror ); } 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 }, {"sasl_bind_s", (PyCFunction)l_ldap_sasl_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.22/Modules/errors.h0000644000175000001440000000060711573747405017736 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.22/Modules/constants.c0000644000175000001440000002266012454256420020423 0ustar michaelusers00000000000000/* constants defined for LDAP * See http://www.python-ldap.org/ for details. * $Id: constants.c,v 1.59 2014/09/12 12:02:21 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); #ifdef LDAP_SCOPE_SUBORDINATE add_int(d,SCOPE_SUBORDINATE); #endif 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_DESC); 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_PEERCERT add_int(d,OPT_X_TLS_PEERCERT); #endif #ifdef LDAP_OPT_X_TLS_VERSION add_int(d,OPT_X_TLS_VERSION); #endif #ifdef LDAP_OPT_X_TLS_CIPHER add_int(d,OPT_X_TLS_CIPHER); #endif #ifdef LDAP_OPT_X_TLS_PEERCERT add_int(d,OPT_X_TLS_PEERCERT); #endif #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.22/Modules/LDAPObject.h0000644000175000001440000000312111573747405020263 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.22/Modules/version.c0000644000175000001440000000066111573747405020102 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.22/Modules/ldapmodule.c0000644000175000001440000000171411573747405020543 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.22/Modules/message.h0000644000175000001440000000052711573747405020047 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.22/Modules/version.h0000644000175000001440000000041611573747405020105 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.22/Modules/berval.c0000644000175000001440000000466711242133473017665 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.22/Modules/ldapcontrol.c0000644000175000001440000002056711652052637020737 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.22/Modules/functions.c0000644000175000001440000000623111573747405020424 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.22/Modules/berval.h0000644000175000001440000000065511242133473017663 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.22/Modules/common.h0000644000175000001440000000143211573747405017707 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.22/Modules/options.h0000644000175000001440000000053111573747405020111 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.22/Modules/options.c0000644000175000001440000002455412454256420020106 0ustar michaelusers00000000000000/* See http://www.python-ldap.org/ for details. * $Id: options.c,v 1.42 2014/09/12 12:02:21 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: case LDAP_OPT_DESC: #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_VERSION case LDAP_OPT_X_TLS_VERSION: #endif #ifdef LDAP_OPT_X_TLS_CIPHER case LDAP_OPT_X_TLS_CIPHER: #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.22/Modules/schema.h0000644000175000001440000000036311573747405017661 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.22/Modules/message.c0000644000175000001440000001536511652052637020042 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.22/Modules/common.c0000644000175000001440000000076411573747405017711 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.22/Modules/errors.c0000644000175000001440000001342712542231503017715 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.25 2015/06/23 09:45:09 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_METHOD_NOT_SUPPORTED); 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_MEMORY); seterrobj(NO_OBJECT_CLASS_MODS); seterrobj(NO_OBJECT_CLASS_MODS); seterrobj(NO_RESULTS_RETURNED); seterrobj(NO_SUCH_ATTRIBUTE); seterrobj(NO_SUCH_OBJECT); seterrobj(NOT_ALLOWED_ON_NONLEAF); seterrobj(NOT_ALLOWED_ON_RDN); seterrobj(NOT_SUPPORTED); 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); seterrobj(VLV_ERROR); seterrobj(X_PROXY_AUTHZ_FAILURE); #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.22/README0000644000175000001440000000706411652052636015516 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.22/Demo/0000755000175000001440000000000012612730505015506 5ustar michaelusers00000000000000python-ldap-2.4.22/Demo/options.py0000644000175000001440000000164107635354564017575 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.22/Demo/ldapcontrols.py0000644000175000001440000000147710256025543020576 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.22/Demo/initialize.py0000644000175000001440000000612512306422607020226 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 CACERTFILE='/etc/ssl/ca-bundle.pem' 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_CACERTFILE,CACERTFILE) # 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() print '***ldap.OPT_X_TLS_VERSION',l.get_option(ldap.OPT_X_TLS_VERSION) print '***ldap.OPT_X_TLS_CIPHER',l.get_option(ldap.OPT_X_TLS_CIPHER) # 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_CACERTFILE,CACERTFILE) # 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('','') print '***ldap.OPT_X_TLS_VERSION',l.get_option(ldap.OPT_X_TLS_VERSION) print '***ldap.OPT_X_TLS_CIPHER',l.get_option(ldap.OPT_X_TLS_CIPHER) # 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.22/Demo/pyasn1/0000755000175000001440000000000012612730505016721 5ustar michaelusers00000000000000python-ldap-2.4.22/Demo/pyasn1/ppolicy.py0000644000175000001440000000274111663516773020774 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.22/Demo/pyasn1/derefcontrol.py0000644000175000001440000000230012577303247021765 0ustar michaelusers00000000000000#!/usr/bin/env python """ This sample script demonstrates the use of the dereference control (see https://tools.ietf.org/html/draft-masarati-ldap-deref) Requires module pyasn1 (see http://pyasn1.sourceforge.net/) """ import pprint,ldap,ldap.modlist,ldap.resiter from ldap.controls.deref import DereferenceControl uri = "ldap://ipa.demo1.freeipa.org" class MyLDAPObject(ldap.ldapobject.LDAPObject,ldap.resiter.ResultProcessor): pass l = MyLDAPObject(uri,trace_level=0) l.simple_bind_s('uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org','Secret123') dc = DereferenceControl( True, { 'member':[ 'uid', 'description', 'cn', 'mail', ], } ) print 'pyasn1 output of request control:' print dc._derefSpecs().prettyPrint() msg_id = l.search_ext( 'dc=demo1,dc=freeipa,dc=org', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)', attrlist=['cn','objectClass','member','description'], serverctrls = [dc] ) for res_type,res_data,res_msgid,res_controls in l.allresults(msg_id,add_ctrls=1): for dn,entry,deref_control in res_data: # process dn and entry print dn,entry['objectClass'] if deref_control: pprint.pprint(deref_control[0].derefRes) python-ldap-2.4.22/Demo/pyasn1/sessiontrack.py0000644000175000001440000000251711604613510022004 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.22/Demo/pyasn1/psearch.py0000644000175000001440000000406711552002163020721 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.22/Demo/pyasn1/readentrycontrol.py0000644000175000001440000000660611652052636022706 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.22/Demo/pyasn1/dds.py0000644000175000001440000000251711663704151020055 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.22/Demo/pyasn1/noopsearch.py0000644000175000001440000000335312612730413021436 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ Demo script for counting searching with OpenLDAP's no-op control 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.openldap import SearchNoOpControl SEARCH_TIMEOUT=30.0 try: ldap_url = ldapurl.LDAPUrl(sys.argv[1]) except IndexError: print 'Usage: noopsearch.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.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) try: msg_id = ldap_conn.search_ext( ldap_url.dn, ldap_url.scope, filterstr=ldap_url.filterstr or '(objectClass=*)', attrlist=['1.1'], timeout=SEARCH_TIMEOUT, serverctrls=[SearchNoOpControl(criticality=True)], ) _,_,_,search_response_ctrls = ldap_conn.result3(msg_id,all=1,timeout=SEARCH_TIMEOUT) except ( ldap.TIMEOUT, ldap.TIMELIMIT_EXCEEDED, ldap.SIZELIMIT_EXCEEDED, ldap.ADMINLIMIT_EXCEEDED),e: ldap_conn.abandon(msg_id) sys.exit(1) noop_srch_ctrl = [ c for c in search_response_ctrls if c.controlType==SearchNoOpControl.controlType ][0] print 'Number of search results: %d' % noop_srch_ctrl.numSearchResults print 'Number of search continuations: %d' % noop_srch_ctrl.numSearchContinuations python-ldap-2.4.22/Demo/pyasn1/syncrepl.py0000644000175000001440000001376412454256417021156 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 close_db(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 syncrepl_refreshdone(self): print 'Initial synchronization is now done, persist phase begins' 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 ): ldap_connection.close_db() 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:' print sys.argv[0], ' ' print sys.argv[0], '\'ldap://127.0.0.1/cn=users,dc=test'\ '?*'\ '?sub'\ '?(objectClass=*)'\ '?bindname=uid=admin%2ccn=users%2cdc=test,'\ 'X-BINDPW=password\' db.shelve' 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', attrlist=ldap_url.attrs, 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.22/Demo/pickle_ldapobject.py0000644000175000001440000000063107650130021021507 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.22/Demo/reconnect.py0000644000175000001440000000103107524237466020051 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.22/Demo/paged_search_ext_s.py0000644000175000001440000000571212306422607021675 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.22/Demo/matchedvalues.py0000644000175000001440000000355410655566727020736 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.22/Demo/schema_tree.py0000644000175000001440000000450710256025756020354 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.22/Demo/ms_ad_bind.py0000644000175000001440000000204311004050475020131 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.22/Demo/rename.py0000644000175000001440000000203107426477316017342 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.22/Demo/simplebrowse.py0000644000175000001440000000615107410130511020565 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.22/Demo/simple.py0000644000175000001440000000450607426477316017375 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.22/Demo/page_control.py0000644000175000001440000000364112454256417020551 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- url = "ldap://localhost:1390" base = "dc=stroeder,dc=de" search_flt = r'(objectClass=*)' page_size = 10 binddn = '' bindpw = '' trace_level = 0 import ldap,pprint try: from ldap.controls.pagedresults import SimplePagedResultsControl except ImportError: from ldap.controls.libldap 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=trace_level) l.protocol_version = 3 l.simple_bind_s(binddn,bindpw) 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 '-'*60 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) print 'rdata=',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.22/Demo/resiter.py0000644000175000001440000000127112454256417017547 0ustar michaelusers00000000000000""" Demo for using ldap.resiter.ResultProcessor written by Michael Stroeder See http://www.python-ldap.org for details. \$Id: resiter.py,v 1.2 2014/11/18 08:27:22 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.22/Demo/passwd_ext_op.py0000644000175000001440000000141510137547500020741 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.22/Demo/schema.py0000644000175000001440000000362511652052636017333 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.22/Demo/sasl_bind.py0000644000175000001440000000363011573747404020033 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.22/Demo/Lib/0000755000175000001440000000000012612730505016214 5ustar michaelusers00000000000000python-ldap-2.4.22/Demo/Lib/ldif/0000755000175000001440000000000012612730505017132 5ustar michaelusers00000000000000python-ldap-2.4.22/Demo/Lib/ldif/ldifcopy.py0000644000175000001440000000124207405752201021315 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.22/Demo/Lib/ldap/0000755000175000001440000000000012612730505017134 5ustar michaelusers00000000000000python-ldap-2.4.22/Demo/Lib/ldap/async/0000755000175000001440000000000012612730505020251 5ustar michaelusers00000000000000python-ldap-2.4.22/Demo/Lib/ldap/async/ldifwriter.py0000644000175000001440000000176110411503653023000 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.22/Demo/Lib/ldap/async/deltree.py0000644000175000001440000000511111573747404022260 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.22/Demo/Lib/ldap/async/sizelimit.py0000644000175000001440000000204010411503653022625 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.22/Demo/Lib/ldapurl/0000755000175000001440000000000012612730505017657 5ustar michaelusers00000000000000python-ldap-2.4.22/Demo/Lib/ldapurl/urlsearch.py0000644000175000001440000000144207617316074022234 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.22/INSTALL0000644000175000001440000000140011573747403015660 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.22/TODO0000644000175000001440000000223112454256417015321 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? - Extract response controls in SASL bind responses - 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.39 2015/01/10 16:58:52 stroeder Exp $ python-ldap-2.4.22/MANIFEST.in0000644000175000001440000000035511652061514016363 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.22/PKG-INFO0000644000175000001440000000263712612730505015727 0ustar michaelusers00000000000000Metadata-Version: 1.1 Name: python-ldap Version: 2.4.22 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.22/LICENCE0000644000175000001440000000113607542145452015617 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.22/CHANGES0000644000175000001440000011463412612725131015625 0ustar michaelusers00000000000000---------------------------------------------------------------- Released 2.4.22 2015-10-25 Changes since 2.4.21: Lib/ * LDIFParser now also accepts value-spec without a space after the colon. * Added key-word argument authz_id to LDAPObject methods sasl_non_interactive_bind_s(), sasl_external_bind_s() and sasl_gssapi_bind_s() * Hmmpf! Added missing self to LDAPObject.fileno(). * ReconnectLDAPObject.sasl_bind_s() now correctly uses generic wrapper arguments *args,**kwargs * LDIFParser.parse_change_records() now correctly calls LDIFParser.handle_change_modify() * Corrected ldap.controls.pwdpolicy.__all__ Doc/ * Started missing docs for sub-module ldap.sasl. ---------------------------------------------------------------- Released 2.4.21 2015-09-25 Changes since 2.4.20: Lib/ * LDAPObject.read_s() now returns None instead of raising ldap.NO_SUCH_OBJECT in case the search operation returned emtpy result. * ldap.resiter.ResultProcessor.allresults() now takes new key-word argument add_ctrls which is internally passed to LDAPObject.result4() and lets the method also return response control along with the search results. * Added ldap.controls.deref implementing support for dereference control Tests/ * Unit tests for module ldif (thanks to Petr Viktorin) ---------------------------------------------------------------- Released 2.4.20 2015-07-07 Changes since 2.4.19: * New wrapping of OpenLDAP's function ldap_sasl_bind_s() allows to intercept the SASL handshake (thanks to René Kijewski) Modules/ * Added exceptions ldap.VLV_ERROR, ldap.X_PROXY_AUTHZ_FAILURE and ldap.AUTH_METHOD_NOT_SUPPORTED Lib/ * Abandoned old syntax when raising ValueError in modules ldif and ldapurl, more information in some exceptions. * ldap.ldapobject.LDAPObject: New convenience methods for SASL GSSAPI or EXTERNAL binds * Refactored parts in ldif.LDIFParser: - New class attributes line_counter and byte_counter contain amount of LDIF data read so far - Renamed some internally used methods - Added support for parsing change records currently limited to changetype: modify - New separate methods parse_entry_records() (also called by parse()) and parse_change_records() - Stricter order checking of dn:, changetype:, etc. - Removed non-existent 'AttrTypeandValueLDIF' from ldif.__all__ * New mix-in class ldap.controls.openldap.SearchNoOpMixIn adds convience method noop_search_st() to LDAPObject class * Added new modules which implement the control classes for Virtual List View (see draft-ietf-ldapext-ldapv3-vlv) and Server-side Sorting (see RFC 2891) (thanks to Benjamin Dauvergne) Note: This is still experimental! Even the API can change later. ---------------------------------------------------------------- Released 2.4.19 2015-01-10 Changes since 2.4.18: Lib/ * Fixed missing ReconnectLDAPObject._reconnect_lock when pickling (see SF#64, thanks to Dan O'Reilly) * Added ldap.controls.pagedresults which is pure Python implementation of Simple Paged Results Control (see RFC 2696) and delivers the correct result size ---------------------------------------------------------------- Released 2.4.18 2014-10-09 Changes since 2.4.17: Lib/ * Fixed raising exception in LDAPObject.read_s() when reading an entry returns empty search result ---------------------------------------------------------------- Released 2.4.17 2014-09-27 Changes since 2.4.16: Lib/ * New hook syncrepl_refreshdone() in ldap.syncrepl.SyncReplConsumer (thanks to Petr Spacek and Chris Mikkelson) Modules/ * Added support for getting file descriptor of connection with ldap.OPT_DESC ---------------------------------------------------------------- Released 2.4.16 2014-09-10 Changes since 2.4.15: Lib/ * New convenience function ldap.dn.is_dn() * New convenience function ldap.escape_str() * New convenience methods LDAPObject.read_s() and LDAPObject.find_unique_entry() * Fixed invoking start_tls_s() in ReconnectLDAPObject.reconnect() (thanks to Philipp Hahn) ---------------------------------------------------------------- Released 2.4.15 2014-03-24 Changes since 2.4.14: Lib/ * Added missing modules ldap.controls.openldap and ldap.controls.pwdpolicy to setup.py * Added missing imports to ldap.controls.pwdpolicy * Fixed ldap.controls.pwdpolicy.decodeControlValue() to decode string of digits * Support for X-SUBST in schema element class LDAPSyntax * Support for X-ORDERED and X-ORIGIN in schema element class AttributeType * ldapurl: New scope 'subordinates' defined in draft-sermersheim-ldap-subordinate-scope Modules/ * New constant ldap.SCOPE_SUBORDINATE derived from ldap.h for draft-sermersheim-ldap-subordinate-scope * Fixed constant ldap.sasl.CB_GETREALM (thanks to Martin Pfeifer) ---------------------------------------------------------------- Released 2.4.14 2014-01-31 Changes since 2.4.13: Lib/ * Added ldap.controls.openldap.SearchNoOpControl * New method ldap.async.AsyncSearchHandler.afterFirstResult() for doing something right after successfully receiving but before processing first result * Better log data written when invoking ldap.LDAPLock.acquire() and ldap.LDAPLock.release() * LDAPObject and friends now pass `desc' to ldap.LDAPLock() which results in better logging * ldapobject.ReconnectLDAPObject now uses internal class-wide lock for serializing reconnects * Method signature of ReconnectLDAPObject.reconnect() changed to be able to call it with separate retry_max and retry_delay values Modules/ * Added support for retrieving negotiated TLS version/cipher with LDAPObject.get_option() with the help of upcoming OpenLDAP libs ---------------------------------------------------------------- Released 2.4.13 2013-06-27 Changes since 2.4.12: Lib/ * ldapobject.ReconnectLDAPObject._apply_last_bind() now sends anonymous simple bind request even if the calling application did not to provoke ldap.SERVER_DOWN in method reconnect() * ldapobject.ReconnectLDAPObject.reconnect() now also catches ldap.TIMEOUT exception after reconnection attempt * Several other fixes for ldapobject.ReconnectLDAPObject (thanks to Jonathan Giannuzzi) ---------------------------------------------------------------- Released 2.4.12 2013-06-01 Changes since 2.4.11: Lib/ * Truly optional import of PyAsn1Error exception which should not fail anymore if pyasn1 is not installed ---------------------------------------------------------------- Released 2.4.11 2013-05-27 Changes since 2.4.10: Lib/ * ldap.controls.DecodeControlTuples() now simply ignores PyAsn1Error exception raised during decoding malformed response control values in case of non-critical controls. * ldif.LDIFWriter.unparse() does not simply skip empty records anymore. ---------------------------------------------------------------- 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.363 2015/10/24 15:55:07 stroeder Exp $ python-ldap-2.4.22/setup.py0000644000175000001440000001333112601272036016333 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.73 2015/09/19 13:38:30 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.deref', 'ldap.controls.libldap', 'ldap.controls.openldap', 'ldap.controls.ppolicy', 'ldap.controls.psearch', 'ldap.controls.pwdpolicy', '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.22/Tests/0000755000175000001440000000000012612730505015724 5ustar michaelusers00000000000000python-ldap-2.4.22/Tests/t_ldapurl.py0000644000175000001440000002244411306033437020271 0ustar michaelusers00000000000000import ldap, unittest import urllib from ldapurl import LDAPUrl class MyLDAPUrl(LDAPUrl): attr2extype = { 'who':'bindname', 'cred':'X-BINDPW', 'start_tls':'startTLS', 'trace_level':'trace', } def sort(l): "Returns a sorted copy of a list" l2 = [e for e in l] l2.sort() return l2 class TestLDAPUrl(unittest.TestCase): def assertNone(self, expr, msg=None): self.failIf(expr is not None, msg or ("%r" % expr)) def test_combo(self): u = MyLDAPUrl("ldap://127.0.0.1:1234/dc=example,dc=com" + "?attr1,attr2,attr3" + "?sub" + "?" + urllib.quote("(objectClass=*)") + "?bindname=" + urllib.quote("cn=d,c=au") + ",X-BINDPW=" + urllib.quote("???") + ",trace=8" ) self.assertEquals(u.urlscheme, "ldap") self.assertEquals(u.hostport, "127.0.0.1:1234") self.assertEquals(u.dn, "dc=example,dc=com") self.assertEquals(u.attrs, ["attr1","attr2","attr3"]) self.assertEquals(u.scope, ldap.SCOPE_SUBTREE) self.assertEquals(u.filterstr, "(objectClass=*)") self.assertEquals(len(u.extensions), 3) self.assertEquals(u.who, "cn=d,c=au") self.assertEquals(u.cred, "???") self.assertEquals(u.trace_level, "8") def test_parse_default_hostport(self): u = LDAPUrl("ldap://") self.assertEquals(u.urlscheme, "ldap") self.assertEquals(u.hostport, "") def test_parse_empty_dn(self): u = LDAPUrl("ldap://") self.assertEquals(u.dn, "") u = LDAPUrl("ldap:///") self.assertEquals(u.dn, "") u = LDAPUrl("ldap:///?") self.assertEquals(u.dn, "") def test_parse_default_attrs(self): u = LDAPUrl("ldap://") self.assertNone(u.attrs) def test_parse_default_scope(self): u = LDAPUrl("ldap://") self.assertNone(u.scope) # RFC4516 s3 def test_parse_default_filter(self): u = LDAPUrl("ldap://") self.assertNone(u.filterstr) # RFC4516 s3 def test_parse_default_extensions(self): u = LDAPUrl("ldap://") self.assertEquals(len(u.extensions), 0) def test_parse_schemes(self): u = LDAPUrl("ldap://") self.assertEquals(u.urlscheme, "ldap") u = LDAPUrl("ldapi://") self.assertEquals(u.urlscheme, "ldapi") u = LDAPUrl("ldaps://") self.assertEquals(u.urlscheme, "ldaps") def test_parse_hostport(self): u = LDAPUrl("ldap://a") self.assertEquals(u.hostport, "a") u = LDAPUrl("ldap://a.b") self.assertEquals(u.hostport, "a.b") u = LDAPUrl("ldap://a.") self.assertEquals(u.hostport, "a.") u = LDAPUrl("ldap://%61%62:%32/") self.assertEquals(u.hostport, "ab:2") u = LDAPUrl("ldap://[::1]/") self.assertEquals(u.hostport, "[::1]") u = LDAPUrl("ldap://[::1]") self.assertEquals(u.hostport, "[::1]") u = LDAPUrl("ldap://[::1]:123/") self.assertEquals(u.hostport, "[::1]:123") u = LDAPUrl("ldap://[::1]:123") self.assertEquals(u.hostport, "[::1]:123") def test_parse_dn(self): u = LDAPUrl("ldap:///") self.assertEquals(u.dn, "") u = LDAPUrl("ldap:///dn=foo") self.assertEquals(u.dn, "dn=foo") u = LDAPUrl("ldap:///dn=foo%2cdc=bar") self.assertEquals(u.dn, "dn=foo,dc=bar") u = LDAPUrl("ldap:///dn=foo%20bar") self.assertEquals(u.dn, "dn=foo bar") u = LDAPUrl("ldap:///dn=foo%2fbar") self.assertEquals(u.dn, "dn=foo/bar") u = LDAPUrl("ldap:///dn=foo%2fbar?") self.assertEquals(u.dn, "dn=foo/bar") u = LDAPUrl("ldap:///dn=foo%3f?") self.assertEquals(u.dn, "dn=foo?") u = LDAPUrl("ldap:///dn=foo%3f") self.assertEquals(u.dn, "dn=foo?") u = LDAPUrl("ldap:///dn=str%c3%b6der.com") self.assertEquals(u.dn, "dn=str\xc3\xb6der.com") def test_parse_attrs(self): u = LDAPUrl("ldap:///?") self.assertEquals(u.attrs, None) u = LDAPUrl("ldap:///??") self.assertEquals(u.attrs, None) u = LDAPUrl("ldap:///?*?") self.assertEquals(u.attrs, ['*']) u = LDAPUrl("ldap:///?*,*?") self.assertEquals(u.attrs, ['*','*']) u = LDAPUrl("ldap:///?a") self.assertEquals(u.attrs, ['a']) u = LDAPUrl("ldap:///?%61") self.assertEquals(u.attrs, ['a']) u = LDAPUrl("ldap:///?a,b") self.assertEquals(u.attrs, ['a','b']) u = LDAPUrl("ldap:///?a%3fb") self.assertEquals(u.attrs, ['a?b']) def test_parse_scope_default(self): u = LDAPUrl("ldap:///??") self.assertNone(u.scope) # on opposite to RFC4516 s3 for referral chasing u = LDAPUrl("ldap:///???") self.assertNone(u.scope) # on opposite to RFC4516 s3 for referral chasing def test_parse_scope(self): u = LDAPUrl("ldap:///??sub") self.assertEquals(u.scope, ldap.SCOPE_SUBTREE) u = LDAPUrl("ldap:///??sub?") self.assertEquals(u.scope, ldap.SCOPE_SUBTREE) u = LDAPUrl("ldap:///??base") self.assertEquals(u.scope, ldap.SCOPE_BASE) u = LDAPUrl("ldap:///??base?") self.assertEquals(u.scope, ldap.SCOPE_BASE) u = LDAPUrl("ldap:///??one") self.assertEquals(u.scope, ldap.SCOPE_ONELEVEL) u = LDAPUrl("ldap:///??one?") self.assertEquals(u.scope, ldap.SCOPE_ONELEVEL) def test_parse_filter(self): u = LDAPUrl("ldap:///???(cn=Bob)") self.assertEquals(u.filterstr, "(cn=Bob)") u = LDAPUrl("ldap:///???(cn=Bob)?") self.assertEquals(u.filterstr, "(cn=Bob)") u = LDAPUrl("ldap:///???(cn=Bob%20Smith)?") self.assertEquals(u.filterstr, "(cn=Bob Smith)") u = LDAPUrl("ldap:///???(cn=Bob/Smith)?") self.assertEquals(u.filterstr, "(cn=Bob/Smith)") u = LDAPUrl("ldap:///???(cn=Bob:Smith)?") self.assertEquals(u.filterstr, "(cn=Bob:Smith)") u = LDAPUrl("ldap:///???&(cn=Bob)(objectClass=user)?") self.assertEquals(u.filterstr, "&(cn=Bob)(objectClass=user)") u = LDAPUrl("ldap:///???|(cn=Bob)(objectClass=user)?") self.assertEquals(u.filterstr, "|(cn=Bob)(objectClass=user)") u = LDAPUrl("ldap:///???(cn=Q%3f)?") self.assertEquals(u.filterstr, "(cn=Q?)") u = LDAPUrl("ldap:///???(cn=Q%3f)") self.assertEquals(u.filterstr, "(cn=Q?)") u = LDAPUrl("ldap:///???(sn=Str%c3%b6der)") # (possibly bad?) self.assertEquals(u.filterstr, "(sn=Str\xc3\xb6der)") u = LDAPUrl("ldap:///???(sn=Str\\c3\\b6der)") self.assertEquals(u.filterstr, "(sn=Str\\c3\\b6der)") # (recommended) u = LDAPUrl("ldap:///???(cn=*\\2a*)") self.assertEquals(u.filterstr, "(cn=*\\2a*)") u = LDAPUrl("ldap:///???(cn=*%5c2a*)") self.assertEquals(u.filterstr, "(cn=*\\2a*)") def test_parse_extensions(self): u = LDAPUrl("ldap:///????") self.assertNone(u.extensions) self.assertNone(u.who) u = LDAPUrl("ldap:///????bindname=cn=root") self.assertEquals(len(u.extensions), 1) self.assertEquals(u.who, "cn=root") u = LDAPUrl("ldap:///????!bindname=cn=root") self.assertEquals(len(u.extensions), 1) self.assertEquals(u.who, "cn=root") u = LDAPUrl("ldap:///????bindname=%3f,X-BINDPW=%2c") self.assertEquals(len(u.extensions), 2) self.assertEquals(u.who, "?") self.assertEquals(u.cred, ",") def test_parse_extensions_nulls(self): u = LDAPUrl("ldap:///????bindname=%00name") self.assertEquals(u.who, "\0name") def test_parse_extensions_5questions(self): u = LDAPUrl("ldap:///????bindname=?") self.assertEquals(len(u.extensions), 1) self.assertEquals(u.who, "?") def test_parse_extensions_novalue(self): u = LDAPUrl("ldap:///????bindname") self.assertEquals(len(u.extensions), 1) self.assertNone(u.who) def test_bad_urls(self): for bad in ("", "ldap:", "ldap:/", ":///", "://", "///", "//", "/", "ldap:///?????", # extension can't start with '?' "LDAP://", "invalid://", "ldap:///??invalid", #XXX-- the following should raise exceptions! "ldap://:389/", # [host [COLON port]] "ldap://a:/", # [host [COLON port]] "ldap://%%%/", # invalid URL encoding "ldap:///?,", # attrdesc *(COMMA attrdesc) "ldap:///?a,", # attrdesc *(COMMA attrdesc) "ldap:///?,a", # attrdesc *(COMMA attrdesc) "ldap:///?a,,b", # attrdesc *(COMMA attrdesc) "ldap://%00/", # RFC4516 2.1 "ldap:///%00", # RFC4516 2.1 "ldap:///?%00", # RFC4516 2.1 "ldap:///??%00", # RFC4516 2.1 "ldap:///????0=0", # extype must start with Alpha "ldap:///????a_b=0", # extype contains only [-a-zA-Z0-9] "ldap:///????!!a=0", # only one exclamation allowed ): try: LDAPUrl(bad) self.fail("should have raised ValueError: %r" % bad) except ValueError: pass if __name__ == '__main__': unittest.main() python-ldap-2.4.22/Tests/slapd.py0000644000175000001440000003163411242117431017403 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.22/Tests/t_cext.py0000644000175000001440000005501311242166311017564 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.22/Tests/search.py0000644000175000001440000000152010772733571017554 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.22/Tests/t_ldif.py0000644000175000001440000000735312612725132017547 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- import unittest import textwrap import ldif try: from StringIO import StringIO except ImportError: from io import StringIO class TestParse(unittest.TestCase): maxDiff = None def _parse_entry_records(self, ldif_string): return ldif.ParseLDIF(StringIO(ldif_string)) def _unparse_entry_records(self, records): f = StringIO() ldif_writer = ldif.LDIFWriter(f) for dn, attrs in records: ldif_writer.unparse(dn, attrs) return f.getvalue() def check_roundtrip(self, ldif_source, entry_records): ldif_source = textwrap.dedent(ldif_source).lstrip() + '\n' parsed_entry_records = self._parse_entry_records(ldif_source) parsed_entry_records2 = self._parse_entry_records( self._unparse_entry_records(entry_records) ) self.assertEqual(parsed_entry_records, entry_records) self.assertEqual(parsed_entry_records2, entry_records) def test_simple(self): self.check_roundtrip(""" dn: cn=x,cn=y,cn=z attrib: value attrib: value2 """, [ ('cn=x,cn=y,cn=z', {'attrib': [b'value', b'value2']}), ]) def test_simple2(self): self.check_roundtrip(""" dn:cn=x,cn=y,cn=z attrib:value attrib:value2 """, [ ('cn=x,cn=y,cn=z', {'attrib': [b'value', b'value2']}), ]) def test_multiple(self): self.check_roundtrip(""" dn: cn=x,cn=y,cn=z a: v attrib: value attrib: value2 dn: cn=a,cn=b,cn=c attrib: value2 attrib: value3 b: v """, [ ('cn=x,cn=y,cn=z', {'attrib': [b'value', b'value2'], 'a': [b'v']}), ('cn=a,cn=b,cn=c', {'attrib': [b'value2', b'value3'], 'b': [b'v']}), ]) def test_folded(self): self.check_roundtrip(""" dn: cn=x,cn=y,cn=z attrib: very long value attrib2: %s """ % ('asdf.' * 20), [ ('cn=x,cn=y,cn=z', {'attrib': [b'verylong value'], 'attrib2': [b'asdf.' * 20]}), ]) def test_empty(self): self.check_roundtrip(""" dn: cn=x,cn=y,cn=z attrib: attrib: foo """, [ ('cn=x,cn=y,cn=z', {'attrib': [b'', b'foo']}), ]) def test_binary(self): self.check_roundtrip(""" dn: cn=x,cn=y,cn=z attrib:: CQAKOiVA """, [ ('cn=x,cn=y,cn=z', {'attrib': [b'\t\0\n:%@']}), ]) def test_binary2(self): self.check_roundtrip(""" dn: cn=x,cn=y,cn=z attrib::CQAKOiVA """, [ ('cn=x,cn=y,cn=z', {'attrib': [b'\t\0\n:%@']}), ]) def test_unicode(self): self.check_roundtrip(""" dn: cn=Michael Stroeder,dc=stroeder,dc=com lastname: Ströder """, [ ('cn=Michael Stroeder,dc=stroeder,dc=com', {'lastname': [b'Str\303\266der']}), ]) def test_sorted(self): self.check_roundtrip(""" dn: cn=x,cn=y,cn=z b: value_b c: value_c a: value_a """, [ ('cn=x,cn=y,cn=z', {'a': [b'value_a'], 'b': [b'value_b'], 'c': [b'value_c']}), ]) if __name__ == '__main__': unittest.main() python-ldap-2.4.22/Tests/t_search.py0000644000175000001440000000565211241722476020103 0ustar michaelusers00000000000000import ldap, unittest import slapd from ldap.ldapobject import LDAPObject server = None class TestSearch(unittest.TestCase): def setUp(self): global server if server is None: server = slapd.Slapd() server.start() base = server.get_dn_suffix() # insert some Foo* objects via ldapadd server.ldapadd("\n".join([ "dn: cn=Foo1,"+base, "objectClass: organizationalRole", "cn: Foo1", "", "dn: cn=Foo2,"+base, "objectClass: organizationalRole", "cn: Foo2", "", "dn: cn=Foo3,"+base, "objectClass: organizationalRole", "cn: Foo3", "", "dn: ou=Container,"+base, "objectClass: organizationalUnit", "ou: Container", "", "dn: cn=Foo4,ou=Container,"+base, "objectClass: organizationalRole", "cn: Foo4", "", ])+"\n") l = LDAPObject(server.get_url()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS,0) l.simple_bind_s(server.get_root_dn(), server.get_root_password()) self.ldap = l self.server = server def test_search_subtree(self): base = self.server.get_dn_suffix() l = self.ldap result = l.search_s(base, ldap.SCOPE_SUBTREE, '(cn=Foo*)', ['*']) result.sort() self.assertEquals(result, [('cn=Foo1,'+base, {'cn': ['Foo1'], 'objectClass': ['organizationalRole']}), ('cn=Foo2,'+base, {'cn': ['Foo2'], 'objectClass': ['organizationalRole']}), ('cn=Foo3,'+base, {'cn': ['Foo3'], 'objectClass': ['organizationalRole']}), ('cn=Foo4,ou=Container,'+base, {'cn': ['Foo4'], 'objectClass': ['organizationalRole']}), ] ) def test_search_onelevel(self): base = self.server.get_dn_suffix() l = self.ldap result = l.search_s(base, ldap.SCOPE_ONELEVEL, '(cn=Foo*)', ['*']) result.sort() self.assertEquals(result, [('cn=Foo1,'+base, {'cn': ['Foo1'], 'objectClass': ['organizationalRole']}), ('cn=Foo2,'+base, {'cn': ['Foo2'], 'objectClass': ['organizationalRole']}), ('cn=Foo3,'+base, {'cn': ['Foo3'], 'objectClass': ['organizationalRole']}), ] ) def test_search_oneattr(self): base = self.server.get_dn_suffix() l = self.ldap result = l.search_s(base, ldap.SCOPE_SUBTREE, '(cn=Foo4)', ['cn']) result.sort() self.assertEquals(result, [('cn=Foo4,ou=Container,'+base, {'cn': ['Foo4']})] ) if __name__ == '__main__': unittest.main() python-ldap-2.4.22/Tests/Lib/0000755000175000001440000000000012612730505016432 5ustar michaelusers00000000000000python-ldap-2.4.22/Tests/Lib/ldap/0000755000175000001440000000000012612730505017352 5ustar michaelusers00000000000000python-ldap-2.4.22/Tests/Lib/ldap/test_modlist.py0000644000175000001440000000614511573747405022460 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.22/Tests/Lib/ldap/schema/0000755000175000001440000000000012612730505020612 5ustar michaelusers00000000000000python-ldap-2.4.22/Tests/Lib/ldap/schema/test_tokenizer.py0000644000175000001440000000256011573747405024254 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.22/Tests/Lib/test_ldapurl.py0000644000175000001440000001117207524433465021523 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.22/Build/0000755000175000001440000000000012612730505015661 5ustar michaelusers00000000000000python-ldap-2.4.22/Build/setup.cfg.win320000644000175000001440000000227607720643662020465 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.22/Build/setup.cfg.mingw0000644000175000001440000000173110175512766020635 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.22/Build/setup.cfg.suse-linux0000644000175000001440000000161207720643662021630 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.22/Lib/0000755000175000001440000000000012612730505015330 5ustar michaelusers00000000000000python-ldap-2.4.22/Lib/dsml.py0000644000175000001440000002007312612725132016643 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.39 2015/09/30 17:15:53 stroeder Exp $ Python compability note: Tested with Python 2.0+. """ __version__ = '2.4.22' 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.22/Lib/ldap/0000755000175000001440000000000012612730505016250 5ustar michaelusers00000000000000python-ldap-2.4.22/Lib/ldap/logger.py0000644000175000001440000000051012541520267020100 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ Helper class for using logging as trace file object """ import logging class logging_file_class: def __init__(self,logging_level): self._logging_level = logging_level def write(self,msg): logging.log(self._logging_level,msg[:-1]) logging_file_obj = logging_file_class(logging.DEBUG) python-ldap-2.4.22/Lib/ldap/sasl.py0000644000175000001440000000777012541520267017602 0ustar michaelusers00000000000000""" sasl.py - support for SASL mechanism See http://www.python-ldap.org/ for details. \$Id: sasl.py,v 1.17 2015/06/06 09:21:38 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 = 0x4008 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.22/Lib/ldap/functions.py0000644000175000001440000000706712541520267020647 0ustar michaelusers00000000000000""" functions.py - wraps functions of module _ldap See http://www.python-ldap.org/ for details. \$Id: functions.py,v 1.31 2015/06/06 09:21:37 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', 'escape_str', ] 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) def escape_str(escape_func,s,*args): """ Applies escape_func() to all items of `args' and returns a string based on format string `s'. """ escape_args = map(escape_func,args) return s % tuple(escape_args) python-ldap-2.4.22/Lib/ldap/cidict.py0000644000175000001440000000522111573747405020075 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.22/Lib/ldap/ldapobject.py0000644000175000001440000011426312612725132020740 0ustar michaelusers00000000000000""" ldapobject.py - wraps class _ldap.LDAPObject See http://www.python-ldap.org/ for details. \$Id: ldapobject.py,v 1.149 2015/10/24 15:46:12 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.sasl,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 NO_UNIQUE_ENTRY(ldap.NO_SUCH_OBJECT): """ Exception raised if a LDAP search returned more than entry entry although assumed to return a unique single search result. """ 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('opcall') 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,desc=''): if ldap.LIBLDAP_R: return ldap.LDAPLock(desc='%s within %s' %(desc,repr(self))) 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 fileno(self): """ Returns file description of LDAP connection. Just a convenience wrapper for LDAPObject.get_option(ldap.OPT_DESC) """ return self.get_option(ldap.OPT_DESC) 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 sasl_non_interactive_bind_s(self,sasl_mech,serverctrls=None,clientctrls=None,sasl_flags=ldap.SASL_QUIET,authz_id=''): """ Send a SASL bind request using a non-interactive SASL method (e.g. GSSAPI, EXTERNAL) """ self.sasl_interactive_bind_s( '', ldap.sasl.sasl( {ldap.sasl.CB_USER:authz_id}, sasl_mech ) ) def sasl_external_bind_s(self,serverctrls=None,clientctrls=None,sasl_flags=ldap.SASL_QUIET,authz_id=''): """ Send SASL bind request using SASL mech EXTERNAL """ self.sasl_non_interactive_bind_s('EXTERNAL',serverctrls,clientctrls,sasl_flags,authz_id) def sasl_gssapi_bind_s(self,serverctrls=None,clientctrls=None,sasl_flags=ldap.SASL_QUIET,authz_id=''): """ Send SASL bind request using SASL mech GSSAPI """ self.sasl_non_interactive_bind_s('GSSAPI',serverctrls,clientctrls,sasl_flags,authz_id) def sasl_bind_s(self,dn,mechanism,cred,serverctrls=None,clientctrls=None): """ sasl_bind_s(dn, mechanism, cred [,serverctrls=None[,clientctrls=None]]) -> int|str """ return self._ldap_call(self._l.sasl_bind_s,dn,mechanism,cred,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls)) 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_s(self,dn,filterstr=None,attrlist=None,serverctrls=None,clientctrls=None,timeout=-1): """ Reads and returns a single entry specified by `dn'. Other attributes just like those passed to `search_ext_s()' """ r = self.search_ext_s( dn, ldap.SCOPE_BASE, filterstr or '(objectClass=*)', attrlist=attrlist, serverctrls=serverctrls, clientctrls=clientctrls, timeout=timeout, ) if r: return r[0][1] else: return None def read_subschemasubentry_s(self,subschemasubentry_dn,attrs=None): """ Returns the sub schema sub entry's data """ try: subschemasubentry = self.read_s( subschemasubentry_dn, filterstr='(objectClass=subschema)', attrlist=attrs or SCHEMA_ATTRS ) except ldap.NO_SUCH_OBJECT: return None else: return subschemasubentry def find_unique_entry(self,base,scope=ldap.SCOPE_SUBTREE,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1): """ Returns a unique entry, raises exception if not unique """ r = self.search_ext_s( base, scope, filterstr, attrlist=attrlist or ['*'], attrsonly=attrsonly, serverctrls=serverctrls, clientctrls=clientctrls, timeout=timeout, sizelimit=2, ) if len(r)!=1: raise NO_UNIQUE_ENTRY('No or non-unique search result for %s' % (repr(filterstr))) return r[0] 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, '_reconnect_lock':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 SimpleLDAPObject.__init__(self,uri,trace_level,trace_file,trace_stack_limit) self._reconnect_lock = ldap.LDAPLock(desc='reconnect lock within %s' % (repr(self))) 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._reconnect_lock = ldap.LDAPLock(desc='reconnect lock within %s' % (repr(self))) self._trace_file = sys.stdout self.reconnect(self._uri) def _store_last_bind(self,method,*args,**kwargs): self._last_bind = (method,args,kwargs) def _apply_last_bind(self): if self._last_bind!=None: func,args,kwargs = self._last_bind func(self,*args,**kwargs) else: # Send explicit anon simple bind request to provoke ldap.SERVER_DOWN in method reconnect() SimpleLDAPObject.simple_bind_s(self,'','') def _restore_options(self): """Restore all recorded options""" for k,v in self._options: SimpleLDAPObject.set_option(self,k,v) def reconnect(self,uri,retry_max=1,retry_delay=60.0): # Drop and clean up old connection completely # Reconnect self._reconnect_lock.acquire() try: reconnect_counter = retry_max while reconnect_counter: counter_text = '%d. (of %d)' % (retry_max-reconnect_counter+1,retry_max) if __debug__ and self._trace_level>=1: self._trace_file.write('*** Trying %s reconnect to %s...\n' % ( counter_text,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: SimpleLDAPObject.start_tls_s(self) # Repeat last simple or SASL bind self._apply_last_bind() except (ldap.SERVER_DOWN,ldap.TIMEOUT),e: if __debug__ and self._trace_level>=1: self._trace_file.write('*** %s reconnect to %s failed\n' % ( counter_text,uri )) reconnect_counter = reconnect_counter-1 if not reconnect_counter: raise e if __debug__ and self._trace_level>=1: self._trace_file.write('=> delay %s...\n' % (retry_delay)) time.sleep(retry_delay) SimpleLDAPObject.unbind_s(self) del self._l else: if __debug__ and self._trace_level>=1: self._trace_file.write('*** %s reconnect to %s successful => repeat last operation\n' % ( counter_text,uri )) self._reconnects_done = self._reconnects_done + 1L break finally: self._reconnect_lock.release() return # reconnect() def _apply_method_s(self,func,*args,**kwargs): if not self.__dict__.has_key('_l'): self.reconnect(self._uri,retry_max=self._retry_max,retry_delay=self._retry_delay) try: return func(self,*args,**kwargs) except ldap.SERVER_DOWN: SimpleLDAPObject.unbind_s(self) del self._l # Try to reconnect self.reconnect(self._uri,retry_max=self._retry_max,retry_delay=self._retry_delay) # 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._store_last_bind(SimpleLDAPObject.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._store_last_bind(SimpleLDAPObject.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._store_last_bind(SimpleLDAPObject.sasl_interactive_bind_s,*args,**kwargs) return res def sasl_bind_s(self,*args,**kwargs): res = self._apply_method_s(SimpleLDAPObject.sasl_bind_s,*args,**kwargs) self._store_last_bind(SimpleLDAPObject.sasl_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.22/Lib/ldap/extop/0000755000175000001440000000000012612730505017407 5ustar michaelusers00000000000000python-ldap-2.4.22/Lib/ldap/extop/dds.py0000644000175000001440000000412011652052637020536 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.22/Lib/ldap/extop/__init__.py0000644000175000001440000000367111652052637021535 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.22/Lib/ldap/async.py0000644000175000001440000002061112541520267017742 0ustar michaelusers00000000000000""" ldap.async - handle async LDAP operations See http://www.python-ldap.org/ for details. \$Id: async.py,v 1.34 2015/06/06 09:21:37 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 self._afterFirstResult = 1 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 ) self._afterFirstResult = 1 return # startSearch() def preProcessing(self): """ Do anything you want after starting search but before receiving and processing results """ def afterFirstResult(self): """ Do anything you want right after successfully receiving but before processing first result """ def postProcessing(self): """ Do anything you want after receiving and processing all 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 self._afterFirstResult: self.afterFirstResult() self._afterFirstResult = 0 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 4-tuple """ result_type,result_list,result_msgid,result_serverctrls,_,_ = self.result4(msgid,0,timeout,add_ctrls=add_ctrls) 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.result4(msgid,0,timeout,add_ctrls=add_ctrls) return # allresults() python-ldap-2.4.22/Lib/ldap/__init__.py0000644000175000001440000000437412612725132020371 0ustar michaelusers00000000000000""" ldap - base module See http://www.python-ldap.org/ for details. $Id: __init__.py,v 1.99 2015/09/30 17:15:53 stroeder Exp $ """ # This is also the overall release version number __version__ = '2.4.22' 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.acquire() %s %s\n' % (self.__class__.__name__,repr(self),self._desc)) return self._lock.acquire() def release(self): if __debug__: global _trace_level if _trace_level>=self._min_trace_level: _trace_file.write('***%s.release() %s %s\n' % (self.__class__.__name__,repr(self),self._desc)) 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,escape_str from ldapobject import NO_UNIQUE_ENTRY 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.22/Lib/ldap/modlist.py0000644000175000001440000001040011573747405020304 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.22/Lib/ldap/syncrepl.py0000644000175000001440000004224112541520267020467 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.7 2015/06/06 09:21:38 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. Only a single syncrepl search may be active on a SyncreplConsumer object. Multiple concurrent syncrepl searches require multiple separate SyncreplConsumer objects and thus multiple connections (LDAPObject instances). """ 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']) if sim.refreshPresent['refreshDone']: self.__refreshDone = True self.syncrepl_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']) if sim.refreshDelete['refreshDone']: self.__refreshDone = True self.syncrepl_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 def syncrepl_refreshdone(self): """ Called by syncrepl_poll() between refresh and persist phase. It indicates that initial synchronization is done and persist phase follows. """ pass python-ldap-2.4.22/Lib/ldap/filter.py0000644000175000001440000000267412541520267020123 0ustar michaelusers00000000000000""" filters.py - misc stuff for handling LDAP filter strings (see RFC2254) See http://www.python-ldap.org/ for details. \$Id: filter.py,v 1.10 2015/06/06 09:21:37 stroeder Exp $ Compability: - Tested with Python 2.0+ """ from ldap import __version__ def escape_filter_chars(assertion_value,escape_mode=0): """ Replace all special characters found in assertion_value by quoted notation. escape_mode If 0 only special chars mentioned in RFC 4515 are escaped. If 1 all NON-ASCII chars are escaped. If 2 all chars are escaped. """ if escape_mode: r = [] if escape_mode==1: for c in assertion_value: if c < '0' or c > '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.22/Lib/ldap/schema/0000755000175000001440000000000012612730505017510 5ustar michaelusers00000000000000python-ldap-2.4.22/Lib/ldap/schema/subentry.py0000644000175000001440000003736612601272036021752 0ustar michaelusers00000000000000""" ldap.schema.subentry - subschema subentry handling See http://www.python-ldap.org/ for details. \$Id: subentry.py,v 1.36 2015/08/08 14:13:30 stroeder Exp $ """ import ldap.cidict,ldap.schema from ldap.schema.models import * from UserDict import UserDict SCHEMA_CLASS_MAPPING = ldap.cidict.cidict() SCHEMA_ATTR_MAPPING = {} for _name in dir(): o = eval(_name) if hasattr(o,'schema_attribute'): SCHEMA_CLASS_MAPPING[o.schema_attribute] = o SCHEMA_ATTR_MAPPING[o] = o.schema_attribute SCHEMA_ATTRS = SCHEMA_CLASS_MAPPING.keys() class SubschemaError(ValueError): pass class OIDNotUnique(SubschemaError): def __init__(self,desc): self.desc = desc def __str__(self): return 'OID not unique for %s' % (self.desc) class NameNotUnique(SubschemaError): def __init__(self,desc): self.desc = desc def __str__(self): return 'NAME not unique for %s' % (self.desc) class SubSchema: """ Arguments: sub_schema_sub_entry Dictionary usually returned by LDAP search or the LDIF parser containing the sub schema sub entry check_uniqueness Defines whether uniqueness of OIDs and NAME is checked. 0 no check 1 check but add schema description with work-around 2 check and raise exception if non-unique OID or NAME is found Class attributes: sed Dictionary holding the subschema information as pre-parsed SchemaElement objects (do not access directly!) name2oid Dictionary holding the mapping from NAMEs to OIDs (do not access directly!) non_unique_oids List of OIDs used at least twice in the subschema non_unique_names List of NAMEs used at least twice in the subschema for the same schema element """ def __init__(self,sub_schema_sub_entry,check_uniqueness=1): # Initialize all dictionaries self.name2oid = {} self.sed = {} self.non_unique_oids = {} self.non_unique_names = {} for c in SCHEMA_CLASS_MAPPING.values(): self.name2oid[c] = ldap.cidict.cidict() self.sed[c] = {} self.non_unique_names[c] = ldap.cidict.cidict() # Transform entry dict to case-insensitive dict e = ldap.cidict.cidict(sub_schema_sub_entry) # Build the schema registry in dictionaries for attr_type in SCHEMA_ATTRS: for attr_value in filter(None,e.get(attr_type,[])): se_class = SCHEMA_CLASS_MAPPING[attr_type] se_instance = se_class(attr_value) se_id = se_instance.get_id() if check_uniqueness and se_id in self.sed[se_class]: self.non_unique_oids[se_id] = None if check_uniqueness==1: # Add to subschema by adding suffix to ID suffix_counter = 1 new_se_id = se_id while new_se_id in self.sed[se_class]: new_se_id = ';'.join((se_id,str(suffix_counter))) suffix_counter += 1 else: se_id = new_se_id elif check_uniqueness>=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: s_temp = None else: if ldap_url.attrs is None: schema_attrs = SCHEMA_ATTRS else: schema_attrs = ldap_url.attrs s_temp = 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,s_temp = ldif_parser.all_records[0] # Work-around for mixed-cased attribute names subschemasubentry_entry = ldap.cidict.cidict() s_temp = s_temp or {} for at,av in s_temp.items(): if at in SCHEMA_CLASS_MAPPING: try: subschemasubentry_entry[at].extend(av) except KeyError: subschemasubentry_entry[at] = av # Finally parse the schema 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.22/Lib/ldap/schema/__init__.py0000644000175000001440000000050211573747405021632 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.22/Lib/ldap/schema/models.py0000644000175000001440000005405012541520267021354 0ustar michaelusers00000000000000""" schema.py - support for subSchemaSubEntry information See http://www.python-ldap.org/ for details. \$Id: models.py,v 1.48 2015/06/06 09:21:38 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 = STRUCTURAL, 1 = ABSTRACT, 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',), 'X-ORIGIN':(None,), 'X-ORDERED':(None,), } 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] self.x_origin = d['X-ORIGIN'][0] self.x_ordered = d['X-ORDERED'][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] ) result.append(self.key_attr('X-ORIGIN',self.x_origin,quoted=1)) result.append(self.key_attr('X-ORDERED',self.x_ordered,quoted=1)) 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,), 'X-BINARY-TRANSFER-REQUIRED':(None,), 'X-SUBST':(None,), } def _set_attrs(self,l,d): self.desc = d['DESC'][0] self.x_subst = d['X-SUBST'][0] self.not_human_readable = \ NOT_HUMAN_READABLE_LDAP_SYNTAXES.has_key(self.oid) or \ d['X-NOT-HUMAN-READABLE'][0]=='TRUE' self.x_binary_transfer_required = d['X-BINARY-TRANSFER-REQUIRED'][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(self.key_attr('X-SUBST',self.x_subst,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.22/Lib/ldap/schema/tokenizer.py0000644000175000001440000000375111573747405022116 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>> s = SSSRequestControl('-cn') ''' controlType = '1.2.840.113556.1.4.473' def __init__( self, criticality=False, ordering_rules=None, ): RequestControl.__init__(self,self.controlType,criticality) self.ordering_rules = ordering_rules if isinstance(ordering_rules, basestring): ordering_rules = [ordering_rules] for rule in ordering_rules: rule = rule.split(':') assert len(rule) < 3, 'syntax for ordering rule: [-][:ordering-rule]' def asn1(self): p = SortKeyListType() for i, rule in enumerate(self.ordering_rules): q = SortKeyType() reverse_order = rule.startswith('-') if reverse_order: rule = rule[1:] if ':' in rule: attribute_type, ordering_rule = rule.split(':') else: attribute_type, ordering_rule = rule, None q.setComponentByName('attributeType', attribute_type) if ordering_rule: q.setComponentByName('orderingRule', ordering_rule) if reverse_order: q.setComponentByName('reverseOrder', 1) p.setComponentByPosition(i, q) return p def encodeControlValue(self): return encoder.encode(self.asn1()) class SortResultType(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType('sortResult', univ.Enumerated().subtype( namedValues=namedval.NamedValues( ('success', 0), ('operationsError', 1), ('timeLimitExceeded', 3), ('strongAuthRequired', 8), ('adminLimitExceeded', 11), ('noSuchAttribute', 16), ('inappropriateMatching', 18), ('insufficientAccessRights', 50), ('busy', 51), ('unwillingToPerform', 53), ('other', 80)), subtypeSpec=univ.Enumerated.subtypeSpec + constraint.SingleValueConstraint( 0, 1, 3, 8, 11, 16, 18, 50, 51, 53, 80))), namedtype.OptionalNamedType('attributeType', univ.OctetString().subtype( implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0) ) )) class SSSResponseControl(ResponseControl): controlType = '1.2.840.113556.1.4.474' def __init__(self,criticality=False): ResponseControl.__init__(self,self.controlType,criticality) def decodeControlValue(self, encoded): p, rest = decoder.decode(encoded, asn1Spec=SortResultType()) assert not rest, 'all data could not be decoded' self.result = int(p.getComponentByName('sortResult')) self.result_code = p.getComponentByName('sortResult').prettyOut(self.result) self.attribute_type_error = p.getComponentByName('attributeType') KNOWN_RESPONSE_CONTROLS[SSSRequestControl.controlType] = SSSRequestControl KNOWN_RESPONSE_CONTROLS[SSSResponseControl.controlType] = SSSResponseControl python-ldap-2.4.22/Lib/ldap/controls/openldap.py0000644000175000001440000000436312612730413022273 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ ldap.controls.openldap - classes for OpenLDAP-specific controls See http://www.python-ldap.org/ for project details. $Id: openldap.py,v 1.6 2015/10/24 16:21:56 stroeder Exp $ """ import ldap.controls from ldap.controls import ValueLessRequestControl,ResponseControl from pyasn1.type import univ from pyasn1.codec.ber import decoder __all__ = [ 'SearchNoOpControl', 'SearchNoOpMixIn', ] class SearchNoOpControl(ValueLessRequestControl,ResponseControl): """ No-op control attached to search operations implementing sort of a count operation see http://www.openldap.org/its/index.cgi?findid=6598 """ controlType = '1.3.6.1.4.1.4203.666.5.18' def __init__(self,criticality=False): self.criticality = criticality class SearchNoOpControlValue(univ.Sequence): pass def decodeControlValue(self,encodedControlValue): decodedValue,_ = decoder.decode(encodedControlValue,asn1Spec=self.SearchNoOpControlValue()) self.resultCode = int(decodedValue[0]) self.numSearchResults = int(decodedValue[1]) self.numSearchContinuations = int(decodedValue[2]) ldap.controls.KNOWN_RESPONSE_CONTROLS[SearchNoOpControl.controlType] = SearchNoOpControl class SearchNoOpMixIn: """ Mix-in class to be used with class LDAPObject and friends. It adds a convenience method noop_search_st() to LDAPObject for easily using the no-op search control. """ def noop_search_st(self,base,scope=ldap.SCOPE_SUBTREE,filterstr='(objectClass=*)',timeout=-1): try: msg_id = self.search_ext( base, scope, filterstr=filterstr, attrlist=['1.1'], timeout=timeout, serverctrls=[SearchNoOpControl(criticality=True)], ) _,_,_,search_response_ctrls = self.result3(msg_id,all=1,timeout=timeout) except ( ldap.TIMEOUT, ldap.TIMELIMIT_EXCEEDED, ldap.SIZELIMIT_EXCEEDED, ldap.ADMINLIMIT_EXCEEDED ),e: self.abandon(msg_id) raise e else: noop_srch_ctrl = [ c for c in search_response_ctrls if c.controlType==SearchNoOpControl.controlType ] if noop_srch_ctrl: return noop_srch_ctrl[0].numSearchResults,noop_srch_ctrl[0].numSearchContinuations else: return (None,None) python-ldap-2.4.22/Lib/ldap/controls/simple.py0000644000175000001440000000762712541520267021775 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ ldap.controls.simple - classes for some very simple LDAP controls See http://www.python-ldap.org/ for details. $Id: simple.py,v 1.10 2015/06/06 09:21:38 stroeder Exp $ """ import struct,ldap from ldap.controls import RequestControl,ResponseControl,LDAPControl,KNOWN_RESPONSE_CONTROLS class ValueLessRequestControl(RequestControl): """ Base class for controls without a controlValue. The presence of the control in a LDAPv3 request changes the server's behaviour when processing the request simply based on the controlType. controlType OID of the request control criticality criticality request control """ def __init__(self,controlType=None,criticality=False): self.controlType = controlType self.criticality = criticality def encodeControlValue(self): return None class OctetStringInteger(LDAPControl): """ Base class with controlValue being unsigend integer values integerValue Integer to be sent as OctetString """ def __init__(self,controlType=None,criticality=False,integerValue=None): self.controlType = controlType self.criticality = criticality self.integerValue = integerValue def encodeControlValue(self): return struct.pack('!Q',self.integerValue) def decodeControlValue(self,encodedControlValue): self.integerValue = struct.unpack('!Q',encodedControlValue)[0] class BooleanControl(LDAPControl): """ Base class for simple request controls with boolean control value. Constructor argument and class attribute: booleanValue Boolean (True/False or 1/0) which is the boolean controlValue. """ boolean2ber = { 1:'\x01\x01\xFF', 0:'\x01\x01\x00' } ber2boolean = { '\x01\x01\xFF':1, '\x01\x01\x00':0 } def __init__(self,controlType=None,criticality=False,booleanValue=False): self.controlType = controlType self.criticality = criticality self.booleanValue = booleanValue def encodeControlValue(self): return self.boolean2ber[int(self.booleanValue)] def decodeControlValue(self,encodedControlValue): self.booleanValue = self.ber2boolean[encodedControlValue] class ManageDSAITControl(ValueLessRequestControl): """ Manage DSA IT Control """ def __init__(self,criticality=False): ValueLessRequestControl.__init__(self,ldap.CONTROL_MANAGEDSAIT,criticality=False) KNOWN_RESPONSE_CONTROLS[ldap.CONTROL_MANAGEDSAIT] = ManageDSAITControl class RelaxRulesControl(ValueLessRequestControl): """ Relax Rules Control """ def __init__(self,criticality=False): ValueLessRequestControl.__init__(self,ldap.CONTROL_RELAX,criticality=False) KNOWN_RESPONSE_CONTROLS[ldap.CONTROL_RELAX] = RelaxRulesControl class ProxyAuthzControl(RequestControl): """ Proxy Authorization Control authzId string containing the authorization ID indicating the identity on behalf which the server should process the request """ def __init__(self,criticality,authzId): RequestControl.__init__(self,ldap.CONTROL_PROXY_AUTHZ,criticality,authzId) class AuthorizationIdentityRequestControl(ValueLessRequestControl): """ Authorization Identity Request and Response Controls """ controlType = '2.16.840.1.113730.3.4.16' def __init__(self,criticality): ValueLessRequestControl.__init__(self,self.controlType,criticality) class AuthorizationIdentityResponseControl(ResponseControl): """ Authorization Identity Request and Response Controls Class attributes: authzId decoded authorization identity """ controlType = '2.16.840.1.113730.3.4.15' def decodeControlValue(self,encodedControlValue): self.authzId = encodedControlValue KNOWN_RESPONSE_CONTROLS[AuthorizationIdentityResponseControl.controlType] = AuthorizationIdentityResponseControl class GetEffectiveRightsControl(RequestControl): """ Get Effective Rights Control """ def __init__(self,criticality,authzId=None): RequestControl.__init__(self,'1.3.6.1.4.1.42.2.27.9.5.2',criticality,authzId) python-ldap-2.4.22/Lib/ldap/controls/pagedresults.py0000644000175000001440000000307012442537417023177 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ ldap.controls.paged - classes for Simple Paged control (see RFC 2696) See http://www.python-ldap.org/ for project details. $Id: pagedresults.py,v 1.2 2014/12/12 10:10:23 stroeder Exp $ """ __all__ = [ 'SimplePagedResultsControl' ] # Imports from python-ldap 2.4+ import ldap.controls from ldap.controls import RequestControl,ResponseControl,KNOWN_RESPONSE_CONTROLS # Imports from pyasn1 from pyasn1.type import tag,namedtype,univ,constraint from pyasn1.codec.ber import encoder,decoder from pyasn1_modules.rfc2251 import LDAPString class PagedResultsControlValue(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType('size',univ.Integer()), namedtype.NamedType('cookie',LDAPString()), ) class SimplePagedResultsControl(RequestControl,ResponseControl): controlType = '1.2.840.113556.1.4.319' def __init__(self,criticality=False,size=10,cookie=''): self.criticality = criticality self.size = size self.cookie = cookie or '' def encodeControlValue(self): pc = PagedResultsControlValue() pc.setComponentByName('size',univ.Integer(self.size)) pc.setComponentByName('cookie',LDAPString(self.cookie)) return encoder.encode(pc) def decodeControlValue(self,encodedControlValue): decodedValue,_ = decoder.decode(encodedControlValue,asn1Spec=PagedResultsControlValue()) self.size = int(decodedValue.getComponentByName('size')) self.cookie = str(decodedValue.getComponentByName('cookie')) KNOWN_RESPONSE_CONTROLS[SimplePagedResultsControl.controlType] = SimplePagedResultsControl python-ldap-2.4.22/Lib/ldap/controls/readentry.py0000644000175000001440000000500712601272036022462 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ ldap.controls.readentry - classes for the Read Entry controls (see RFC 4527) See http://www.python-ldap.org/ for project details. $Id: readentry.py,v 1.5 2015/09/18 17:24:55 stroeder Exp $ """ import ldap from pyasn1.codec.ber import encoder,decoder from ldap.controls import LDAPControl,KNOWN_RESPONSE_CONTROLS from pyasn1_modules.rfc2251 import AttributeDescriptionList,SearchResultEntry class ReadEntryControl(LDAPControl): """ Base class for read entry control described in RFC 4527 attrList list of attribute type names requested Class attributes with values extracted from the response control: dn string holding the distinguished name of the LDAP entry entry dictionary holding the LDAP entry """ def __init__(self,criticality=False,attrList=None): self.criticality,self.attrList,self.entry = criticality,attrList or [],None def encodeControlValue(self): attributeSelection = AttributeDescriptionList() for i in range(len(self.attrList)): attributeSelection.setComponentByPosition(i,self.attrList[i]) return encoder.encode(attributeSelection) def decodeControlValue(self,encodedControlValue): decodedEntry,_ = decoder.decode(encodedControlValue,asn1Spec=SearchResultEntry()) self.dn = str(decodedEntry[0]) self.entry = {} for attr in decodedEntry[1]: self.entry[str(attr[0])] = [ str(attr_value) for attr_value in attr[1] ] class PreReadControl(ReadEntryControl): """ Class for pre-read control described in RFC 4527 attrList list of attribute type names requested Class attributes with values extracted from the response control: dn string holding the distinguished name of the LDAP entry before the operation was done by the server entry dictionary holding the LDAP entry before the operation was done by the server """ controlType = ldap.CONTROL_PRE_READ KNOWN_RESPONSE_CONTROLS[PreReadControl.controlType] = PreReadControl class PostReadControl(ReadEntryControl): """ Class for post-read control described in RFC 4527 attrList list of attribute type names requested Class attributes with values extracted from the response control: dn string holding the distinguished name of the LDAP entry after the operation was done by the server entry dictionary holding the LDAP entry after the operation was done by the server """ controlType = ldap.CONTROL_POST_READ KNOWN_RESPONSE_CONTROLS[PostReadControl.controlType] = PostReadControl python-ldap-2.4.22/Lib/ldap/controls/libldap.py0000644000175000001440000000417412541520267022105 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ controls.libldap - LDAP controls wrapper classes with en-/decoding done by OpenLDAP functions See http://www.python-ldap.org/ for details. $Id: libldap.py,v 1.3 2015/06/06 09:21:38 stroeder Exp $ """ import _ldap,ldap from ldap.controls import RequestControl,LDAPControl,KNOWN_RESPONSE_CONTROLS class AssertionControl(RequestControl): """ LDAP Assertion control, as defined in RFC 4528 filterstr LDAP filter string specifying which assertions have to match so that the server processes the operation """ controlType = ldap.CONTROL_ASSERT def __init__(self,criticality=True,filterstr='(objectClass=*)'): self.criticality = criticality self.filterstr = filterstr def encodeControlValue(self): return _ldap.encode_assertion_control(self.filterstr) KNOWN_RESPONSE_CONTROLS[ldap.CONTROL_ASSERT] = AssertionControl class MatchedValuesControl(RequestControl): """ LDAP Matched Values control, as defined in RFC 3876 filterstr LDAP filter string specifying which attribute values should be returned """ controlType = ldap.CONTROL_VALUESRETURNFILTER def __init__(self,criticality=False,filterstr='(objectClass=*)'): self.criticality = criticality self.filterstr = filterstr def encodeControlValue(self): return _ldap.encode_valuesreturnfilter_control(self.filterstr) KNOWN_RESPONSE_CONTROLS[ldap.CONTROL_VALUESRETURNFILTER] = MatchedValuesControl class SimplePagedResultsControl(LDAPControl): """ LDAP Control Extension for Simple Paged Results Manipulation size Page size requested (number of entries to be returned) cookie Cookie string received with last page """ controlType = ldap.CONTROL_PAGEDRESULTS def __init__(self,criticality=False,size=None,cookie=None): self.criticality = criticality self.size,self.cookie = size,cookie def encodeControlValue(self): return _ldap.encode_page_control(self.size,self.cookie) def decodeControlValue(self,encodedControlValue): self.size,self.cookie = _ldap.decode_page_control(encodedControlValue) KNOWN_RESPONSE_CONTROLS[ldap.CONTROL_PAGEDRESULTS] = SimplePagedResultsControl python-ldap-2.4.22/Lib/ldap/controls/__init__.py0000644000175000001440000001032212541520267022225 0ustar michaelusers00000000000000# -*- coding: utf-8 -*- """ controls.py - support classes for LDAP controls See http://www.python-ldap.org/ for details. $Id: __init__.py,v 1.10 2015/06/06 09:21:38 stroeder Exp $ Description: The ldap.controls module provides LDAPControl classes. Each class provides support for a certain control. """ from ldap import __version__ __all__ = [ 'KNOWN_RESPONSE_CONTROLS', # Classes 'AssertionControl', 'BooleanControl', 'LDAPControl', 'ManageDSAITControl', 'MatchedValuesControl', 'RelaxRulesControl', 'RequestControl', 'ResponseControl', 'SimplePagedResultsControl', 'ValueLessRequestControl', # Functions 'RequestControlTuples', 'DecodeControlTuples', ] # response control OID to class registry KNOWN_RESPONSE_CONTROLS = {} import _ldap,ldap try: from pyasn1.error import PyAsn1Error except ImportError: PyAsn1Error = None class RequestControl: """ Base class for all request controls controlType OID as string of the LDAPv3 extended request control criticality sets the criticality of the control (boolean) encodedControlValue control value of the LDAPv3 extended request control (here it is the BER-encoded ASN.1 control value) """ def __init__(self,controlType=None,criticality=False,encodedControlValue=None): self.controlType = controlType self.criticality = criticality self.encodedControlValue = encodedControlValue def encodeControlValue(self): """ sets class attribute encodedControlValue to the BER-encoded ASN.1 control value composed by class attributes set before """ return self.encodedControlValue class ResponseControl: """ Base class for all response controls controlType OID as string of the LDAPv3 extended response control criticality sets the criticality of the received control (boolean) """ def __init__(self,controlType=None,criticality=False): self.controlType = controlType self.criticality = criticality def decodeControlValue(self,encodedControlValue): """ decodes the BER-encoded ASN.1 control value and sets the appropriate class attributes """ self.encodedControlValue = encodedControlValue class LDAPControl(RequestControl,ResponseControl): """ Base class for combined request/response controls mainly for backward-compability to python-ldap 2.3.x """ def __init__(self,controlType=None,criticality=False,controlValue=None,encodedControlValue=None): self.controlType = controlType self.criticality = criticality self.controlValue = controlValue self.encodedControlValue = encodedControlValue def RequestControlTuples(ldapControls): """ Return list of readily encoded 3-tuples which can be directly passed to C module _ldap ldapControls sequence-type of RequestControl objects """ if ldapControls is None: return None else: result = [ (c.controlType,c.criticality,c.encodeControlValue()) for c in ldapControls ] return result def DecodeControlTuples(ldapControlTuples,knownLDAPControls=None): """ Returns list of readily decoded ResponseControl objects ldapControlTuples Sequence-type of 3-tuples returned by _ldap.result4() containing the encoded ASN.1 control values of response controls. knownLDAPControls Dictionary mapping extended control's OID to ResponseControl class of response controls known by the application. If None ldap.controls.KNOWN_RESPONSE_CONTROLS is used here. """ knownLDAPControls = knownLDAPControls or KNOWN_RESPONSE_CONTROLS result = [] for controlType,criticality,encodedControlValue in ldapControlTuples or []: try: control = knownLDAPControls[controlType]() except KeyError: if criticality: raise ldap.UNAVAILABLE_CRITICAL_EXTENSION('Received unexpected critical response control with controlType %s' % (repr(controlType))) else: control.controlType,control.criticality = controlType,criticality try: control.decodeControlValue(encodedControlValue) except PyAsn1Error,e: if criticality: raise e else: result.append(control) return result # Import the standard sub-modules from ldap.controls.simple import * from ldap.controls.libldap import * python-ldap-2.4.22/Lib/ldap/dn.py0000644000175000001440000000571412541520267017235 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.13 2015/06/06 09:21:37 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] def is_dn(s): """ Returns True is `s' can be parsed by ldap.dn.dn2str() like as a distinguished host_name (DN), otherwise False is returned. """ try: dn2str(s) except Exception: return False else: return True python-ldap-2.4.22/Lib/ldapurl.py0000644000175000001440000002711312612725132017351 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.74 2015/09/30 17:15:53 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.22' __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 LDAP_SCOPE_SUBORDINATES = 3 SEARCH_SCOPE_STR = {None:'',0:'base',1:'one',2:'sub',3:'subordinates'} SEARCH_SCOPE = { '':None, # the search scope strings defined in RFC 4516 'base':LDAP_SCOPE_BASE, 'one':LDAP_SCOPE_ONELEVEL, 'sub':LDAP_SCOPE_SUBTREE, # from draft-sermersheim-ldap-subordinate-scope 'subordinates':LDAP_SCOPE_SUBORDINATES, } # 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('Value %s for ldap_url does not seem to be a LDAP URL.' % (repr(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('Invalid search scope %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.22/Lib/python_ldap.egg-info/0000755000175000001440000000000012612730505021343 5ustar michaelusers00000000000000python-ldap-2.4.22/Lib/python_ldap.egg-info/not-zip-safe0000644000175000001440000000000112601272427023573 0ustar michaelusers00000000000000 python-ldap-2.4.22/Lib/python_ldap.egg-info/top_level.txt0000644000175000001440000000003512612730505024073 0ustar michaelusers00000000000000_ldap dsml ldap ldapurl ldif python-ldap-2.4.22/Lib/python_ldap.egg-info/dependency_links.txt0000644000175000001440000000000112612730505025411 0ustar michaelusers00000000000000 python-ldap-2.4.22/Lib/python_ldap.egg-info/PKG-INFO0000644000175000001440000000263712612730505022450 0ustar michaelusers00000000000000Metadata-Version: 1.1 Name: python-ldap Version: 2.4.22 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.22/Lib/python_ldap.egg-info/SOURCES.txt0000644000175000001440000000471512612730505023236 0ustar michaelusers00000000000000CHANGES 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 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/ldap/async/deltree.py Demo/Lib/ldap/async/ldifwriter.py Demo/Lib/ldap/async/sizelimit.py Demo/Lib/ldapurl/urlsearch.py Demo/Lib/ldif/ldifcopy.py Demo/pyasn1/dds.py Demo/pyasn1/derefcontrol.py Demo/pyasn1/noopsearch.py Demo/pyasn1/ppolicy.py Demo/pyasn1/psearch.py Demo/pyasn1/readentrycontrol.py Demo/pyasn1/sessiontrack.py Demo/pyasn1/syncrepl.py Lib/dsml.py Lib/ldapurl.py Lib/ldif.py 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/deref.py Lib/ldap/controls/libldap.py Lib/ldap/controls/openldap.py Lib/ldap/controls/pagedresults.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/controls/sss.py Lib/ldap/controls/vlv.py Lib/ldap/extop/__init__.py Lib/ldap/extop/dds.py 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/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/search.py Tests/slapd.py Tests/t_cext.py Tests/t_ldapurl.py Tests/t_ldif.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.22/Lib/python_ldap.egg-info/requires.txt0000644000175000001440000000001312612730505023735 0ustar michaelusers00000000000000setuptools python-ldap-2.4.22/Lib/ldif.py0000644000175000001440000004252412612726757016645 0ustar michaelusers00000000000000""" ldif - generate and parse LDIF data (see RFC 2849) See http://www.python-ldap.org/ for details. $Id: ldif.py,v 1.87 2015/10/24 16:12:31 stroeder Exp $ Python compability note: Tested with Python 2.0+, but should work with Python 1.5.2+. """ __version__ = '2.4.22' __all__ = [ # constants 'ldif_pattern', # functions '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, # ldap.MOD_REPLACE 'delete' :1, # ldap.MOD_DELETE 'replace':2, # ldap.MOD_REPLACE } 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 _unfold_lines(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