+#endif
+
+#define PYLDAP_FUNC(rtype) rtype
+#define PYLDAP_DATA(rtype) extern rtype
+
+PYLDAP_FUNC(PyObject *) LDAPerror_TypeError(const char *, PyObject *);
+
+PYLDAP_FUNC(void) LDAPadd_methods(PyObject *d, PyMethodDef *methods);
+
+#define PyNone_Check(o) ((o) == Py_None)
+
+/* *** berval *** */
+PYLDAP_FUNC(PyObject *) LDAPberval_to_object(const struct berval *bv);
+PYLDAP_FUNC(PyObject *) LDAPberval_to_unicode_object(const struct berval *bv);
+
+/* *** constants *** */
+PYLDAP_FUNC(int) LDAPinit_constants(PyObject *m);
+
+PYLDAP_DATA(PyObject *) LDAPexception_class;
+PYLDAP_FUNC(PyObject *) LDAPerror(LDAP *);
+PYLDAP_FUNC(PyObject *) LDAPraise_for_message(LDAP *, LDAPMessage *m);
+PYLDAP_FUNC(PyObject *) LDAPerr(int errnum);
+
+#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 */
+
+/* *** functions *** */
+PYLDAP_FUNC(void) LDAPinit_functions(PyObject *);
+
+/* *** ldapcontrol *** */
+PYLDAP_FUNC(void) LDAPinit_control(PyObject *d);
+PYLDAP_FUNC(void) LDAPControl_List_DEL(LDAPControl **);
+PYLDAP_FUNC(int) LDAPControls_from_object(PyObject *, LDAPControl ***);
+PYLDAP_FUNC(PyObject *) LDAPControls_to_List(LDAPControl **ldcs);
+
+/* *** ldapobject *** */
+typedef struct {
+ PyObject_HEAD LDAP *ldap;
+ PyThreadState *_save; /* for thread saving on referrals */
+ int valid;
+} LDAPObject;
+
+PYLDAP_DATA(PyTypeObject) LDAP_Type;
+PYLDAP_FUNC(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); \
+ PyThreadState *_save = lo->_save; \
+ lo->_save = NULL; \
+ PyEval_RestoreThread( _save ); \
+ }
+
+/* *** messages *** */
+PYLDAP_FUNC(PyObject *)
+LDAPmessage_to_python(LDAP *ld, LDAPMessage *m, int add_ctrls,
+ int add_intermediates);
+
+/* *** options *** */
+PYLDAP_FUNC(int) LDAP_optionval_by_name(const char *name);
+PYLDAP_FUNC(int) LDAP_set_option(LDAPObject *self, int option,
+ PyObject *value);
+PYLDAP_FUNC(PyObject *) LDAP_get_option(LDAPObject *self, int option);
+PYLDAP_FUNC(void) set_timeval_from_double(struct timeval *tv, double d);
+
+#endif /* pythonldap_h */
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..752b1394
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,13 @@
+# Security Policy
+
+## Supported Versions
+
+Security updates are applied only to the latest release.
+
+## Reporting a Vulnerability
+
+If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
+
+Please disclose it at our [security advisory](https://github.com/python-ldap/python-ldap/security/advisories/new).
+
+This project is maintained by a team of volunteers on a reasonable-effort basis. As such, vulnerabilities will be disclosed in a best effort base.
diff --git a/Tests/t_ldap_options.py b/Tests/t_ldap_options.py
index 89f21a43..e9bef591 100644
--- a/Tests/t_ldap_options.py
+++ b/Tests/t_ldap_options.py
@@ -23,8 +23,8 @@
])
TEST_CTRL_EXPECTED = [
TEST_CTRL[0],
- # get_option returns empty bytes
- (TEST_CTRL[1][0], TEST_CTRL[1][1], b''),
+ # Noop has no value
+ (TEST_CTRL[1][0], TEST_CTRL[1][1], None),
]
diff --git a/Tests/t_ldapobject.py b/Tests/t_ldapobject.py
index 3bcc00a2..ada5f990 100644
--- a/Tests/t_ldapobject.py
+++ b/Tests/t_ldapobject.py
@@ -3,9 +3,11 @@
See https://www.python-ldap.org/ for details.
"""
+import base64
import errno
import linecache
import os
+import re
import socket
import unittest
import pickle
@@ -20,6 +22,11 @@
from slapdtest import requires_ldapi, requires_sasl, requires_tls
from slapdtest import requires_init_fd
+PEM_CERT_RE = re.compile(
+ b'-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----',
+ re.DOTALL
+)
+
LDIF_TEMPLATE = """dn: %(suffix)s
objectClass: dcObject
@@ -421,6 +428,33 @@ def test_multiple_starttls(self):
l.simple_bind_s(self.server.root_dn, self.server.root_pw)
self.assertEqual(l.whoami_s(), 'dn:' + self.server.root_dn)
+ @requires_tls()
+ @unittest.skipUnless(
+ hasattr(ldap, "OPT_X_TLS_PEERCERT"),
+ reason="Requires OPT_X_TLS_PEERCERT"
+ )
+ def test_get_tls_peercert(self):
+ l = self.ldap_object_class(self.server.ldap_uri)
+ peercert = l.get_option(ldap.OPT_X_TLS_PEERCERT)
+ self.assertEqual(peercert, None)
+ with self.assertRaises(ValueError):
+ l.set_option(ldap.OPT_X_TLS_PEERCERT, b"")
+
+ l.set_option(ldap.OPT_X_TLS_CACERTFILE, self.server.cafile)
+ l.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
+ l.start_tls_s()
+
+ peercert = l.get_option(ldap.OPT_X_TLS_PEERCERT)
+ self.assertTrue(peercert)
+ self.assertIsInstance(peercert, bytes)
+
+ with open(self.server.servercert, "rb") as f:
+ server_cert = f.read()
+ pem_body = PEM_CERT_RE.search(server_cert).group(1)
+ server_der = base64.b64decode(pem_body)
+
+ self.assertEqual(server_der, peercert)
+
def test_dse(self):
dse = self._ldap_conn.read_rootdse_s()
self.assertIsInstance(dse, dict)
@@ -613,24 +647,14 @@ def test105_reconnect_restore(self):
@requires_init_fd()
class Test03_SimpleLDAPObjectWithFileno(Test00_SimpleLDAPObject):
def _open_ldap_conn(self, who=None, cred=None, **kwargs):
- if hasattr(self, '_sock'):
- raise RuntimeError("socket already connected")
- self._sock = socket.create_connection(
+ sock = socket.create_connection(
(self.server.hostname, self.server.port)
)
- return super()._open_ldap_conn(
- who=who, cred=cred, fileno=self._sock.fileno(), **kwargs
+ result = super()._open_ldap_conn(
+ who=who, cred=cred, fileno=sock.fileno(), **kwargs
)
-
- def tearDown(self):
- self._sock.close()
- del self._sock
- super().tearDown()
-
- def reset_connection(self):
- self._sock.close()
- del self._sock
- super(Test03_SimpleLDAPObjectWithFileno, self).reset_connection()
+ sock.detach()
+ return result
if __name__ == '__main__':
diff --git a/pyproject.toml b/pyproject.toml
index dda8dbc1..75f7c06a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,3 @@
-[tool.black]
-line-length = 88
-target-version = ['py36', 'py37', 'py38']
-
[tool.isort]
line_length=88
known_first_party=['ldap', '_ldap', 'ldapurl', 'ldif', 'slapdtest']
diff --git a/setup.cfg b/setup.cfg
index 01d43a06..fdb32fbc 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -16,14 +16,16 @@ license_file = LICENCE
# These defines needs OpenLDAP built with
# ./configure --with-cyrus-sasl --with-tls
-defines = HAVE_SASL HAVE_TLS HAVE_LIBLDAP_R
+defines = HAVE_SASL HAVE_TLS
extra_compile_args =
extra_objects =
+# Uncomment this if your libldap is not thread-safe and you need libldap_r
+# instead
# Example for full-featured build:
# Support for StartTLS/LDAPS, SASL bind and reentrant libldap_r.
-libs = ldap_r lber
+#libs = ldap_r lber
# Installation options
[install]
@@ -33,7 +35,7 @@ optimize = 1
# Linux distributors/packagers should adjust these settings
[bdist_rpm]
provides = python-ldap
-requires = python libldap-2_4
+requires = python libldap-2
vendor = python-ldap project
packager = python-ldap team
distribution_name = openSUSE 11.x
diff --git a/setup.py b/setup.py
index 119b5715..ea7364cd 100644
--- a/setup.py
+++ b/setup.py
@@ -86,10 +86,11 @@ class OpenLDAP2:
'Programming Language :: Python',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
- 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
+ 'Programming Language :: Python :: 3.10',
+ 'Programming Language :: Python :: 3.11',
+ 'Programming Language :: Python :: 3.12',
+ 'Programming Language :: Python :: 3.13',
# Note: when updating Python versions, also change tox.ini and .github/workflows/*
'Topic :: Database',
@@ -114,15 +115,8 @@ class OpenLDAP2:
'Modules/berval.c',
],
depends = [
- 'Modules/LDAPObject.h',
- 'Modules/berval.h',
- 'Modules/common.h',
+ 'Modules/pythonldap.h',
'Modules/constants_generated.h',
- 'Modules/constants.h',
- 'Modules/functions.h',
- 'Modules/ldapcontrol.h',
- 'Modules/message.h',
- 'Modules/options.h',
],
libraries = LDAP_CLASS.libs,
include_dirs = ['Modules'] + LDAP_CLASS.include_dirs,
@@ -132,7 +126,6 @@ class OpenLDAP2:
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)] + \
[
@@ -154,6 +147,7 @@ class OpenLDAP2:
'ldap.extop',
'ldap.schema',
'slapdtest',
+ 'slapdtest.certs',
],
package_dir = {'': 'Lib',},
data_files = LDAP_CLASS.extra_files,
@@ -163,6 +157,6 @@ class OpenLDAP2:
'pyasn1_modules >= 0.1.5',
],
zip_safe=False,
- python_requires='>=3.6',
+ python_requires='>=3.9',
test_suite = 'Tests',
)
diff --git a/tox.ini b/tox.ini
index aaef8b5a..0b284a4e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,19 +5,21 @@
[tox]
# Note: when updating Python versions, also change setup.py and .github/worlflows/*
-envlist = py{36,37,38,39,310},c90-py{36,37},py3-nosasltls,doc,py3-trace,pypy3
+envlist = py{39,310,311,312},py3-nosasltls,doc,py3-trace,pypy3.9
minver = 1.8
[gh-actions]
python =
- 3.6: py36
- 3.7: py37
- 3.8: py38, doc, py3-nosasltls
- 3.9: py39, py3-trace
- pypy3: pypy3
+ 3.9: py39, py3-trace, doc, py3-nosasltls
+ 3.10: py310
+ 3.11: py311
+ 3.12: py312
+ 3.13: py313
+ pypy3.9: pypy3.9
+ pypy3.10: pypy3.10
[testenv]
-deps =
+deps = setuptools
passenv = WITH_GCOV
# - Enable BytesWarning
# - Turn all warnings into exceptions.
@@ -26,6 +28,11 @@ setenv =
commands = {envpython} -bb -Werror \
-m unittest discover -v -s Tests -p 't_*' {posargs}
+[testenv:py312]
+# Python 3.12 headers are incompatible with declaration-after-statement
+setenv =
+ CFLAGS=-Wno-int-in-bool-context -Werror -std=c99
+
[testenv:py3-nosasltls]
basepython = python3
# don't install, install dependencies manually
@@ -90,6 +97,7 @@ deps =
markdown
sphinx
sphinxcontrib-spelling
+ setuptools
commands =
{envpython} setup.py check --restructuredtext --metadata --strict
{envpython} -m markdown README -f {envtmpdir}/README.html
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy