Skip to content

Add wrapper for ldap_connect #534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Doc/reference/ldap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,16 @@ and wait for and return with the server's result, or with
The *dn* and *attr* arguments are text strings; see :ref:`bytes_mode`.


.. py:method:: LDAPObject.connect() -> None

Opens a connection to the server if one is not established already. If that
fails, an instance of :py:exc:`ldap.LDAPError` or appropriate subtype is
raised.

Requires libldap 2.5+ and will fail with :py:exc:`NotImplementedError`
if that is not met.


.. py:method:: LDAPObject.delete(dn) -> int

.. py:method:: LDAPObject.delete_s(dn) -> None
Expand Down
7 changes: 7 additions & 0 deletions Lib/ldap/ldapobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ def fileno(self):
"""
return self.get_option(ldap.OPT_DESC)

def connect(self):
"""
connect() -> None
Establishes LDAP connection if needed.
"""
return self._ldap_call(self._l.connect)

def abandon_ext(self,msgid,serverctrls=None,clientctrls=None):
"""
abandon_ext(msgid[,serverctrls=None[,clientctrls=None]]) -> None
Expand Down
33 changes: 33 additions & 0 deletions Modules/LDAPObject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1505,6 +1505,38 @@ l_ldap_extended_operation(LDAPObject *self, PyObject *args)
return PyInt_FromLong(msgid);
}

/* ldap_connect */

static PyObject *
l_ldap_connect(LDAPObject *self, PyObject Py_UNUSED(args))
{
#if LDAP_VENDOR_VERSION >= 20500
int ldaperror;

if (ldap_version_info.ldapai_vendor_version < 20500)
#endif
{
PyErr_SetString(PyExc_NotImplementedError,
"loaded libldap doesn't support this feature");
return NULL;
}

#if LDAP_VENDOR_VERSION >= 20500
if (not_valid(self))
return NULL;

LDAP_BEGIN_ALLOW_THREADS(self);
ldaperror = ldap_connect(self->ldap);
LDAP_END_ALLOW_THREADS(self);

if ( ldaperror != LDAP_SUCCESS )
return LDAPerror(self->ldap);

Py_INCREF(Py_None);
return Py_None;
#endif
}

/* methods */

static PyMethodDef methods[] = {
Expand Down Expand Up @@ -1534,6 +1566,7 @@ static PyMethodDef methods[] = {
{"cancel", (PyCFunction)l_ldap_cancel, METH_VARARGS},
#endif
{"extop", (PyCFunction)l_ldap_extended_operation, METH_VARARGS},
{"connect", (PyCFunction)l_ldap_connect, METH_NOARGS},
{NULL, NULL}
};

Expand Down
8 changes: 8 additions & 0 deletions Modules/constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,14 @@ LDAPinit_constants(PyObject *m)
if (PyModule_AddIntConstant(m, "LIBLDAP_R", thread_safe) != 0)
return -1;

if (ldap_get_option(NULL, LDAP_OPT_API_INFO, &ldap_version_info) != LDAP_SUCCESS) {
PyErr_SetString(PyExc_ImportError, "unrecognised libldap version");
return -1;
}
if (PyModule_AddIntConstant(m, "_VENDOR_VERSION_RUNTIME",
ldap_version_info.ldapai_vendor_version ) != 0)
return -1;

/* Generated constants -- see Lib/ldap/constants.py */

#define add_err(n) do { \
Expand Down
2 changes: 2 additions & 0 deletions Modules/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ extern PyObject *LDAPerror(LDAP *);
extern PyObject *LDAPraise_for_message(LDAP *, LDAPMessage *m);
PyObject *LDAPerr(int errnum);

extern LDAPAPIInfo ldap_version_info;

#ifndef LDAP_CONTROL_PAGE_OID
#define LDAP_CONTROL_PAGE_OID "1.2.840.113556.1.4.319"
#endif /* !LDAP_CONTROL_PAGE_OID */
Expand Down
4 changes: 4 additions & 0 deletions Modules/ldapmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ PyMODINIT_FUNC init_ldap(void);
#define _STR(x) #x
#define STR(x) _STR(x)

LDAPAPIInfo ldap_version_info = {
.ldapai_info_version = LDAP_API_INFO_VERSION,
};

static char version_str[] = STR(LDAPMODULE_VERSION);
static char author_str[] = STR(LDAPMODULE_AUTHOR);
static char license_str[] = STR(LDAPMODULE_LICENSE);
Expand Down
23 changes: 23 additions & 0 deletions Tests/t_cext.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,29 @@ def test_simple_anonymous_bind(self):
self.assertEqual(pmsg, [])
self.assertEqual(ctrls, [])

@unittest.skipUnless(
_ldap.VENDOR_VERSION >= 20500 and \
_ldap._VENDOR_VERSION_RUNTIME >= 20500,
reason="Test requires libldap 2.5+"
)
def test_connect(self):
l = self._open_conn(bind=False)
invalid_fileno = l.get_option(_ldap.OPT_DESC)
l.connect()
fileno = l.get_option(_ldap.OPT_DESC)
self.assertNotEqual(invalid_fileno, fileno)

self._bind_conn(l)

@unittest.skipUnless(
_ldap._VENDOR_VERSION_RUNTIME < 20500,
reason="Test requires linking to libldap < 2.5"
)
def test_connect_notimpl(self):
l = self._open_conn(bind=False)
with self.assertRaises(NotImplementedError):
l.connect()

def test_anon_rootdse_search(self):
l = self._open_conn(bind=False)
# see if we can get the rootdse with anon search (without prior bind)
Expand Down