From 57ed6aece2ddcc307e99e88c2a32548bed80b1b9 Mon Sep 17 00:00:00 2001 From: SOMA Yuki Date: Thu, 2 Aug 2018 18:39:01 +0900 Subject: [PATCH 1/9] Attach response controls to exceptions --- Doc/reference/ldap.rst | 1 + Doc/reference/slapdtest.rst | 6 +++ Lib/slapdtest/__init__.py | 5 ++- Lib/slapdtest/_slapdtest.py | 71 +++++++++++++++++++++++++++++++- Modules/LDAPObject.c | 30 ++++++++------ Modules/constants.c | 18 +++++++- Modules/constants.h | 1 + Tests/t_ldap_controls_ppolicy.py | 6 +++ Tests/t_ldapobject.py | 71 +++++++++++++++++++++++++++++++- 9 files changed, 190 insertions(+), 19 deletions(-) diff --git a/Doc/reference/ldap.rst b/Doc/reference/ldap.rst index 0ce2e418..d9e0c5da 100644 --- a/Doc/reference/ldap.rst +++ b/Doc/reference/ldap.rst @@ -316,6 +316,7 @@ The module defines the following exceptions: is set to a truncated form of the name provided or alias dereferenced for the lowest entry (object or alias) that was matched. + The :py:const:`ctrls` field can be included to the dictionary, which is a list of response controls. .. py:exception:: ADMINLIMIT_EXCEEDED diff --git a/Doc/reference/slapdtest.rst b/Doc/reference/slapdtest.rst index bd54bb69..824c5dd9 100644 --- a/Doc/reference/slapdtest.rst +++ b/Doc/reference/slapdtest.rst @@ -26,3 +26,9 @@ Classes .. autoclass:: slapdtest.SlapdTestCase :members: + +.. autoclass:: slapdtest.PPolicyEnabledSlapdObject + :members: + +.. autoclass:: slapdtest.PPolicyEnabledSlapdTestCase + :members: diff --git a/Lib/slapdtest/__init__.py b/Lib/slapdtest/__init__.py index 56ba2c91..567e666b 100644 --- a/Lib/slapdtest/__init__.py +++ b/Lib/slapdtest/__init__.py @@ -7,6 +7,9 @@ __version__ = '3.1.0' -from slapdtest._slapdtest import SlapdObject, SlapdTestCase, SysLogHandler +from slapdtest._slapdtest import ( + SlapdObject, SlapdTestCase, + PPolicyEnabledSlapdObject, PPolicyEnabledSlapdTestCase, + SysLogHandler) from slapdtest._slapdtest import requires_ldapi, requires_sasl, requires_tls from slapdtest._slapdtest import skip_unless_ci diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index f1885caf..93e9d0a2 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -27,7 +27,9 @@ # a template string for generating simple slapd.conf file SLAPD_CONF_TEMPLATE = r""" + serverID %(serverid)s +%(moduleload_directives)s moduleload back_%(database)s %(include_directives)s loglevel %(loglevel)s @@ -43,6 +45,8 @@ rootdn "%(rootdn)s" rootpw "%(rootpw)s" +%(overlay_configurations)s + TLSCACertificateFile "%(cafile)s" TLSCertificateFile "%(servercert)s" TLSCertificateKeyFile "%(serverkey)s" @@ -187,6 +191,9 @@ class SlapdObject(object): 'core.schema', ) + modules = () + overlays = () + TMPDIR = os.environ.get('TMP', os.getcwd()) if 'SCHEMA' in os.environ: SCHEMADIR = os.environ['SCHEMA'] @@ -331,9 +338,22 @@ def gen_config(self): ) for schema_file in self.openldap_schema_files ) + + moduleload_directives = '\n'.join( + "moduleload {module}".format(module=module) + for module in self.modules + ) + + overlay_configurations = '\n'.join( + "overlay {name}\n{configuration}".format(**overlay) + for overlay in self.overlays + ) + config_dict = { 'serverid': hex(self.server_id), 'schema_prefix':self._schema_prefix, + 'moduleload_directives': moduleload_directives, + 'overlay_configurations': overlay_configurations, 'include_directives': include_directives, 'loglevel': self.slapd_loglevel, 'database': self.database, @@ -582,10 +602,17 @@ def _open_ldap_conn(self, who=None, cred=None, **kwargs): """ return a LDAPObject instance after simple bind """ + ldap_conn = self._make_ldap_object(**kwargs) + ldap_conn.simple_bind_s(who or self.server.root_dn, cred or self.server.root_pw) + return ldap_conn + + def _make_ldap_object(self, **kwargs): + """ + return an unbound LDAPObject instance with common ldap options. + """ ldap_conn = self.ldap_object_class(self.server.ldap_uri, **kwargs) ldap_conn.protocol_version = 3 - #ldap_conn.set_option(ldap.OPT_REFERRALS, 0) - ldap_conn.simple_bind_s(who or self.server.root_dn, cred or self.server.root_pw) + # ldap_conn.set_option(ldap.OPT_REFERRALS, 0) return ldap_conn @classmethod @@ -596,3 +623,43 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.server.stop() + + +class PPolicyEnabledSlapdObject(SlapdObject): + """ + A subclass of :py:class:`SlapdObject` with password policy enabled. + Note that this class has no actual password policy configuration entries. + It is the job of the users of this class to define + the default password policies on their own. + The dn of the default is :attr:`.default_ppolicy_dn` of this class. + """ + + openldap_schema_files = ( + 'core.schema', 'ppolicy.schema' + ) + modules = ( + 'ppolicy', + ) + + default_ppolicy_dn = "cn=default-ppolicy,%(suffix)s" % { + 'suffix': SlapdObject.suffix + } + + overlays = ( + { + 'name': 'ppolicy', + 'configuration': "\n".join([ + 'ppolicy_default "{}"'.format(default_ppolicy_dn), + # let slapd tell the clients that they are locked out + 'ppolicy_use_lockout']) + }, + ) + + +class PPolicyEnabledSlapdTestCase(SlapdTestCase): + """ + A subclass of :py:class:`SlapdTestCase`, which uses + :py:class:`PPolicyEnabledSlapdObject` as the slapd controller. + """ + + server_class = PPolicyEnabledSlapdObject diff --git a/Modules/LDAPObject.c b/Modules/LDAPObject.c index bc26727e..0d980959 100644 --- a/Modules/LDAPObject.c +++ b/Modules/LDAPObject.c @@ -1162,6 +1162,20 @@ l_ldap_result4(LDAPObject *self, PyObject *args) LDAP_END_ALLOW_THREADS(self); } + if (!(pyctrls = LDAPControls_to_List(serverctrls))) { + int err = LDAP_NO_MEMORY; + + LDAP_BEGIN_ALLOW_THREADS(self); + ldap_set_option(self->ldap, LDAP_OPT_ERROR_NUMBER, &err); + LDAP_END_ALLOW_THREADS(self); + ldap_msgfree(msg); + Py_XDECREF(valuestr); + return LDAPerror(self->ldap, "LDAPControls_to_List"); + } + ldap_controls_free(serverctrls); + + /* Always call Py_XDECREF(pyctrls) before returning after here */ + if (result != LDAP_SUCCESS) { /* result error */ char *e, err[1024]; @@ -1173,21 +1187,11 @@ l_ldap_result4(LDAPObject *self, PyObject *args) e = "ldap_parse_result"; ldap_msgfree(msg); Py_XDECREF(valuestr); - return LDAPerror(self->ldap, e); + retval = LDAPraise_exception(self->ldap, e, pyctrls); + Py_XDECREF(pyctrls); + return retval; } - if (!(pyctrls = LDAPControls_to_List(serverctrls))) { - int err = LDAP_NO_MEMORY; - - LDAP_BEGIN_ALLOW_THREADS(self); - ldap_set_option(self->ldap, LDAP_OPT_ERROR_NUMBER, &err); - LDAP_END_ALLOW_THREADS(self); - ldap_msgfree(msg); - Py_XDECREF(valuestr); - return LDAPerror(self->ldap, "LDAPControls_to_List"); - } - ldap_controls_free(serverctrls); - pmsg = LDAPmessage_to_python(self->ldap, msg, add_ctrls, add_intermediates); diff --git a/Modules/constants.c b/Modules/constants.c index f8da3736..bcd5dff2 100644 --- a/Modules/constants.c +++ b/Modules/constants.c @@ -48,7 +48,7 @@ LDAPerr(int errnum) /* Convert an LDAP error into an informative python exception */ PyObject * -LDAPerror(LDAP *l, char *msg) +LDAPraise_exception(LDAP *l, char *msg, PyObject *pyctrls) { if (l == NULL) { PyErr_SetFromErrno(LDAPexception_class); @@ -104,6 +104,11 @@ LDAPerror(LDAP *l, char *msg) ldap_memfree(matched); } + if (pyctrls != NULL) { + PyDict_SetItemString(info, "ctrls", pyctrls); + /* Py_XDECREF(pyctrls) must be called on caller side */ + } + if (errnum == LDAP_REFERRAL) { str = PyUnicode_FromString(msg); if (str) @@ -125,6 +130,17 @@ LDAPerror(LDAP *l, char *msg) } } + +/* Convert an LDAP error into an informative python exception. + This is the convenient function for the case where the exception + doesn't have to include any response controls. */ +PyObject * +LDAPerror(LDAP *l, char *msg) +{ + return LDAPraise_exception(l, msg, NULL); +} + + /* initialise the module constants */ int diff --git a/Modules/constants.h b/Modules/constants.h index 8a390b5b..3b66c1d3 100644 --- a/Modules/constants.h +++ b/Modules/constants.h @@ -12,6 +12,7 @@ extern PyObject *LDAPconstant(int); extern PyObject *LDAPexception_class; extern PyObject *LDAPerror(LDAP *, char *msg); +extern PyObject *LDAPraise_exception(LDAP *, char *msg, PyObject *pyctrls); PyObject *LDAPerr(int errnum); #ifndef LDAP_CONTROL_PAGE_OID diff --git a/Tests/t_ldap_controls_ppolicy.py b/Tests/t_ldap_controls_ppolicy.py index 8644e563..d20ee500 100644 --- a/Tests/t_ldap_controls_ppolicy.py +++ b/Tests/t_ldap_controls_ppolicy.py @@ -9,6 +9,7 @@ PP_GRACEAUTH = b'0\x84\x00\x00\x00\t\xa0\x84\x00\x00\x00\x03\x81\x01\x02' PP_TIMEBEFORE = b'0\x84\x00\x00\x00\t\xa0\x84\x00\x00\x00\x03\x80\x012' +PP_ACCOUNT_LOCKOUT = b'0\x03\x81\x01\x01' class TestControlsPPolicy(unittest.TestCase): @@ -28,6 +29,11 @@ def test_ppolicy_timebefore(self): pp.decodeControlValue(PP_TIMEBEFORE) self.assertPPolicy(pp, timeBeforeExpiration=50) + def test_ppolicy_account_lockout(self): + pp = ppolicy.PasswordPolicyControl() + pp.decodeControlValue(PP_ACCOUNT_LOCKOUT) + self.assertPPolicy(pp, error=1) + if __name__ == '__main__': unittest.main() diff --git a/Tests/t_ldapobject.py b/Tests/t_ldapobject.py index 0619d514..2f79cef6 100644 --- a/Tests/t_ldapobject.py +++ b/Tests/t_ldapobject.py @@ -28,9 +28,10 @@ os.environ['LDAPNOINIT'] = '1' import ldap +import ldap.controls +import ldap.controls.ppolicy from ldap.ldapobject import SimpleLDAPObject, ReconnectLDAPObject - -from slapdtest import SlapdTestCase +from slapdtest import SlapdTestCase, PPolicyEnabledSlapdTestCase from slapdtest import requires_ldapi, requires_sasl, requires_tls @@ -75,6 +76,72 @@ """ +class Test02_ResponseControl(PPolicyEnabledSlapdTestCase): + """ + tests abount response controls sent by the server + """ + + ldap_object_class = SimpleLDAPObject + + @classmethod + def setUpClass(cls): + super(Test02_ResponseControl, cls).setUpClass() + # insert some Foo* objects via ldapadd + cls.server.ldapadd( + LDIF_TEMPLATE % { + 'suffix': cls.server.suffix, + 'rootdn': cls.server.root_dn, + 'rootcn': cls.server.root_cn, + 'rootpw': cls.server.root_pw, + 'dc': cls.server.suffix.split(',')[0][3:], + } + ) + + # Very strict pwdMaxFailure in order to easily test the cases where + # bind failure with response controls is needed + cls.server.ldapadd( + '''dn: {dn} +objectClass: organizationalRole +objectClass: pwdPolicy +cn: default-ppolicy +pwdAttribute: userPassword +pwdLockout: TRUE +pwdMaxFailure: 1 +pwdLockoutDuration: 60 +pwdFailureCountInterval: 3600'''.format(dn=cls.server.default_ppolicy_dn) + ) + + def test_response_controls_are_attached_to_exceptions(self): + base = self.server.suffix + cn = "test_response_controls_are_attached_to_exceptions" + user_dn = "cn={},{}".format(cn, base) + password = "user5_pw" + + self.server.ldapadd( + '''dn: {dn} +objectClass: applicationProcess +objectClass: simpleSecurityObject +cn: {cn} +userPassword: {password}'''.format(cn=cn, dn=user_dn, password=password) + ) + + ldap_conn = self._make_ldap_object(bytes_mode=False) + + # Firstly cause a bind failure to lock out the account + with self.assertRaises(ldap.INVALID_CREDENTIALS): + wrong_password = 'wrong' + password + ldap_conn.simple_bind_s(user_dn, wrong_password) + + with self.assertRaises(ldap.INVALID_CREDENTIALS) as cm: + ldap_conn.simple_bind_s( + user_dn, password, + serverctrls=[ldap.controls.ppolicy.PasswordPolicyControl()]) + + controls = cm.exception.args[0]['ctrls'] + pp = ldap.controls.DecodeControlTuples(controls)[0] + self.assertEqual(pp.error, 1) # error == 1 means AccountLockout + + class Test00_SimpleLDAPObject(SlapdTestCase): """ test LDAP search operations From c76f08792cf74cd5a4f7b274e70de274441984dc Mon Sep 17 00:00:00 2001 From: SOMA Yuki Date: Wed, 21 Nov 2018 15:04:06 +0900 Subject: [PATCH 2/9] remove PPolicyEnabledSlapdTestCase --- Lib/slapdtest/__init__.py | 2 +- Lib/slapdtest/_slapdtest.py | 9 --------- Tests/t_ldapobject.py | 5 +++-- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Lib/slapdtest/__init__.py b/Lib/slapdtest/__init__.py index 567e666b..bf8dad03 100644 --- a/Lib/slapdtest/__init__.py +++ b/Lib/slapdtest/__init__.py @@ -9,7 +9,7 @@ from slapdtest._slapdtest import ( SlapdObject, SlapdTestCase, - PPolicyEnabledSlapdObject, PPolicyEnabledSlapdTestCase, + PPolicyEnabledSlapdObject, SysLogHandler) from slapdtest._slapdtest import requires_ldapi, requires_sasl, requires_tls from slapdtest._slapdtest import skip_unless_ci diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index 93e9d0a2..3a820b0f 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -654,12 +654,3 @@ class PPolicyEnabledSlapdObject(SlapdObject): 'ppolicy_use_lockout']) }, ) - - -class PPolicyEnabledSlapdTestCase(SlapdTestCase): - """ - A subclass of :py:class:`SlapdTestCase`, which uses - :py:class:`PPolicyEnabledSlapdObject` as the slapd controller. - """ - - server_class = PPolicyEnabledSlapdObject diff --git a/Tests/t_ldapobject.py b/Tests/t_ldapobject.py index 2f79cef6..5633866a 100644 --- a/Tests/t_ldapobject.py +++ b/Tests/t_ldapobject.py @@ -31,7 +31,7 @@ import ldap.controls import ldap.controls.ppolicy from ldap.ldapobject import SimpleLDAPObject, ReconnectLDAPObject -from slapdtest import SlapdTestCase, PPolicyEnabledSlapdTestCase +from slapdtest import SlapdTestCase, PPolicyEnabledSlapdObject from slapdtest import requires_ldapi, requires_sasl, requires_tls @@ -76,12 +76,13 @@ """ -class Test02_ResponseControl(PPolicyEnabledSlapdTestCase): +class Test02_ResponseControl(SlapdTestCase): """ tests abount response controls sent by the server """ ldap_object_class = SimpleLDAPObject + server_class = PPolicyEnabledSlapdObject @classmethod def setUpClass(cls): From cc7ef3b45ad087128db19dc0031467d56844b15b Mon Sep 17 00:00:00 2001 From: SOMA Yuki Date: Wed, 21 Nov 2018 15:17:21 +0900 Subject: [PATCH 3/9] avoid to export PPolicyEnabledSlapdObject --- Lib/slapdtest/__init__.py | 5 +---- Lib/slapdtest/_slapdtest.py | 31 ------------------------------- Tests/t_ldapobject.py | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/Lib/slapdtest/__init__.py b/Lib/slapdtest/__init__.py index bf8dad03..56ba2c91 100644 --- a/Lib/slapdtest/__init__.py +++ b/Lib/slapdtest/__init__.py @@ -7,9 +7,6 @@ __version__ = '3.1.0' -from slapdtest._slapdtest import ( - SlapdObject, SlapdTestCase, - PPolicyEnabledSlapdObject, - SysLogHandler) +from slapdtest._slapdtest import SlapdObject, SlapdTestCase, SysLogHandler from slapdtest._slapdtest import requires_ldapi, requires_sasl, requires_tls from slapdtest._slapdtest import skip_unless_ci diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index 3a820b0f..b0f1042e 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -623,34 +623,3 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.server.stop() - - -class PPolicyEnabledSlapdObject(SlapdObject): - """ - A subclass of :py:class:`SlapdObject` with password policy enabled. - Note that this class has no actual password policy configuration entries. - It is the job of the users of this class to define - the default password policies on their own. - The dn of the default is :attr:`.default_ppolicy_dn` of this class. - """ - - openldap_schema_files = ( - 'core.schema', 'ppolicy.schema' - ) - modules = ( - 'ppolicy', - ) - - default_ppolicy_dn = "cn=default-ppolicy,%(suffix)s" % { - 'suffix': SlapdObject.suffix - } - - overlays = ( - { - 'name': 'ppolicy', - 'configuration': "\n".join([ - 'ppolicy_default "{}"'.format(default_ppolicy_dn), - # let slapd tell the clients that they are locked out - 'ppolicy_use_lockout']) - }, - ) diff --git a/Tests/t_ldapobject.py b/Tests/t_ldapobject.py index 5633866a..9f503f87 100644 --- a/Tests/t_ldapobject.py +++ b/Tests/t_ldapobject.py @@ -31,7 +31,7 @@ import ldap.controls import ldap.controls.ppolicy from ldap.ldapobject import SimpleLDAPObject, ReconnectLDAPObject -from slapdtest import SlapdTestCase, PPolicyEnabledSlapdObject +from slapdtest import SlapdTestCase, SlapdObject from slapdtest import requires_ldapi, requires_sasl, requires_tls @@ -76,6 +76,37 @@ """ +class PPolicyEnabledSlapdObject(SlapdObject): + """ + A subclass of :py:class:`SlapdObject` with password policy enabled. + Note that this class has no actual password policy configuration entries. + It is the job of the users of this class to define + the default password policies on their own. + The dn of the default is :attr:`.default_ppolicy_dn` of this class. + """ + + openldap_schema_files = ( + 'core.schema', 'ppolicy.schema' + ) + modules = ( + 'ppolicy', + ) + + default_ppolicy_dn = "cn=default-ppolicy,%(suffix)s" % { + 'suffix': SlapdObject.suffix + } + + overlays = ( + { + 'name': 'ppolicy', + 'configuration': "\n".join([ + 'ppolicy_default "{}"'.format(default_ppolicy_dn), + # let slapd tell the clients that they are locked out + 'ppolicy_use_lockout']) + }, + ) + + class Test02_ResponseControl(SlapdTestCase): """ tests abount response controls sent by the server From 0144bdb17f82695989507b8e34f80f588fa5ce11 Mon Sep 17 00:00:00 2001 From: SOMA Yuki Date: Wed, 21 Nov 2018 15:18:04 +0900 Subject: [PATCH 4/9] trim a redundant comment --- Modules/constants.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/constants.c b/Modules/constants.c index bcd5dff2..5f8fc321 100644 --- a/Modules/constants.c +++ b/Modules/constants.c @@ -106,7 +106,6 @@ LDAPraise_exception(LDAP *l, char *msg, PyObject *pyctrls) if (pyctrls != NULL) { PyDict_SetItemString(info, "ctrls", pyctrls); - /* Py_XDECREF(pyctrls) must be called on caller side */ } if (errnum == LDAP_REFERRAL) { From a0ba3993602892956ea68a690c526c4498250b0a Mon Sep 17 00:00:00 2001 From: SOMA Yuki Date: Wed, 21 Nov 2018 15:20:55 +0900 Subject: [PATCH 5/9] Test: assert len(ctrls) & remove a magic number --- Tests/t_ldapobject.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tests/t_ldapobject.py b/Tests/t_ldapobject.py index 9f503f87..879fcee0 100644 --- a/Tests/t_ldapobject.py +++ b/Tests/t_ldapobject.py @@ -170,8 +170,11 @@ def test_response_controls_are_attached_to_exceptions(self): serverctrls=[ldap.controls.ppolicy.PasswordPolicyControl()]) controls = cm.exception.args[0]['ctrls'] - pp = ldap.controls.DecodeControlTuples(controls)[0] - self.assertEqual(pp.error, 1) # error == 1 means AccountLockout + decoded_controls = ldap.controls.DecodeControlTuples(controls) + self.assertEqual(len(decoded_controls), 1) + pp = decoded_controls[0] + expected_error = ldap.controls.ppolicy.PasswordPolicyError('accountLocked') + self.assertEqual(pp.error, int(expected_error)) class Test00_SimpleLDAPObject(SlapdTestCase): From 2b5d1eeca5acc5c4f6e1f13d969ec84ce5d82bef Mon Sep 17 00:00:00 2001 From: SOMA Yuki Date: Wed, 21 Nov 2018 16:56:55 +0900 Subject: [PATCH 6/9] Doc: delete about slapdtest.PPolicyEnabled... --- Doc/reference/slapdtest.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Doc/reference/slapdtest.rst b/Doc/reference/slapdtest.rst index 824c5dd9..90c671ad 100644 --- a/Doc/reference/slapdtest.rst +++ b/Doc/reference/slapdtest.rst @@ -27,8 +27,3 @@ Classes .. autoclass:: slapdtest.SlapdTestCase :members: -.. autoclass:: slapdtest.PPolicyEnabledSlapdObject - :members: - -.. autoclass:: slapdtest.PPolicyEnabledSlapdTestCase - :members: From a616cae85e6985989ed4b2c86d4392941de3bb1a Mon Sep 17 00:00:00 2001 From: SOMA Yuki Date: Wed, 21 Nov 2018 17:25:32 +0900 Subject: [PATCH 7/9] Doc: slapdtest: modules and overlay attributes --- Lib/slapdtest/_slapdtest.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index b0f1042e..a9aa8f44 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -191,7 +191,17 @@ class SlapdObject(object): 'core.schema', ) + #: List (or tuple) of OpenLDAP module names you want to activate. + #: Default is empty. modules = () + + #: List (or tuple) of OpenLDAP overlay settings you want to include. + #: Default is empty. + #: Each element is a dict of the form of:: + #: + #: {"name": overlay_name, + #: "configuration": configuration_text} + #: overlays = () TMPDIR = os.environ.get('TMP', os.getcwd()) From e8648f239b55e19785be823a220ea223c1c620bd Mon Sep 17 00:00:00 2001 From: SOMA Yuki Date: Wed, 21 Nov 2018 18:17:06 +0900 Subject: [PATCH 8/9] delete _make_ldap_object() --- Lib/slapdtest/_slapdtest.py | 11 ++--------- Tests/t_ldapobject.py | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index a9aa8f44..5b566c35 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -612,17 +612,10 @@ def _open_ldap_conn(self, who=None, cred=None, **kwargs): """ return a LDAPObject instance after simple bind """ - ldap_conn = self._make_ldap_object(**kwargs) - ldap_conn.simple_bind_s(who or self.server.root_dn, cred or self.server.root_pw) - return ldap_conn - - def _make_ldap_object(self, **kwargs): - """ - return an unbound LDAPObject instance with common ldap options. - """ ldap_conn = self.ldap_object_class(self.server.ldap_uri, **kwargs) ldap_conn.protocol_version = 3 - # ldap_conn.set_option(ldap.OPT_REFERRALS, 0) + #ldap_conn.set_option(ldap.OPT_REFERRALS, 0) + ldap_conn.simple_bind_s(who or self.server.root_dn, cred or self.server.root_pw) return ldap_conn @classmethod diff --git a/Tests/t_ldapobject.py b/Tests/t_ldapobject.py index 879fcee0..2a023642 100644 --- a/Tests/t_ldapobject.py +++ b/Tests/t_ldapobject.py @@ -157,7 +157,7 @@ def test_response_controls_are_attached_to_exceptions(self): userPassword: {password}'''.format(cn=cn, dn=user_dn, password=password) ) - ldap_conn = self._make_ldap_object(bytes_mode=False) + ldap_conn = self.ldap_object_class(self.server.ldap_uri) # Firstly cause a bind failure to lock out the account with self.assertRaises(ldap.INVALID_CREDENTIALS): From 9ed502d34c801ef258cf52032971a76cda1473f1 Mon Sep 17 00:00:00 2001 From: SOMA Yuki Date: Wed, 21 Nov 2018 18:32:44 +0900 Subject: [PATCH 9/9] Test: check also empty control list --- Tests/t_ldapobject.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Tests/t_ldapobject.py b/Tests/t_ldapobject.py index 2a023642..90392258 100644 --- a/Tests/t_ldapobject.py +++ b/Tests/t_ldapobject.py @@ -160,10 +160,13 @@ def test_response_controls_are_attached_to_exceptions(self): ldap_conn = self.ldap_object_class(self.server.ldap_uri) # Firstly cause a bind failure to lock out the account - with self.assertRaises(ldap.INVALID_CREDENTIALS): + with self.assertRaises(ldap.INVALID_CREDENTIALS) as cm: wrong_password = 'wrong' + password ldap_conn.simple_bind_s(user_dn, wrong_password) + empty_controls = cm.exception.args[0]['ctrls'] + self.assertEqual(len(empty_controls), 0) + with self.assertRaises(ldap.INVALID_CREDENTIALS) as cm: ldap_conn.simple_bind_s( user_dn, password,