Skip to content
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
18 changes: 18 additions & 0 deletions Doc/reference/ldap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,16 @@ the following option identifiers are defined as constants:

.. py:data:: OPT_CLIENT_CONTROLS

Client-side request controls that libldap attaches to every operation
on the connection. :py:meth:`LDAPObject.set_option` takes a list of
:py:class:`ldap.controls.RequestControl` instances, the module-level
:py:func:`set_option` takes the encoded 3-tuples. Response controls
never show up here, they are returned by :py:meth:`LDAPObject.result3`
and :py:meth:`LDAPObject.result4`. :py:meth:`LDAPObject.get_option`
decodes the stored controls with the response control classes, which
only works for controls whose request and response values share a
schema, see https://github.com/python-ldap/python-ldap/issues/643.

.. py:data:: OPT_DEBUG_LEVEL

Sets the debug level within the underlying OpenLDAP C lib (libldap).
Expand Down Expand Up @@ -220,6 +230,14 @@ the following option identifiers are defined as constants:

.. py:data:: OPT_SERVER_CONTROLS

Server-side request controls that libldap attaches to every operation
on the connection unless instructed otherwise (e.g. with
`search_ext(..., serverctrls=[...], ...)`), for example
:py:class:`ldap.controls.sessiontrack.SessionTrackingControl`.
Same rules as :py:data:`OPT_CLIENT_CONTROLS`. Controls meant for a
single operation belong in the ``serverctrls`` argument of that
operation instead.

.. py:data:: OPT_SIZELIMIT

.. py:data:: OPT_SUCCESS
Expand Down
51 changes: 42 additions & 9 deletions Tests/t_ldap_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
from slapdtest import SlapdTestCase, requires_tls

import ldap
from ldap.controls import RequestControlTuples
from ldap.controls import RequestControl, RequestControlTuples
from ldap.controls.pagedresults import SimplePagedResultsControl
from ldap.controls.openldap import SearchNoOpControl
from ldap.ldapobject import SimpleLDAPObject

from pyasn1.error import PyAsn1Error


SENTINEL = object()

Expand Down Expand Up @@ -181,15 +183,46 @@ def test_network_timeout_attribute(self):
finally:
self.set_option(option, old)

# test is failing with:
# pyasn1.error.SubstrateUnderrunError: Short octet stream on tag decoding
@unittest.expectedFailure
def test_client_controls(self):
self._test_controls(ldap.OPT_CLIENT_CONTROLS)
# These options hold request controls, but get_option() decodes them
# with the response classes in KNOWN_RESPONSE_CONTROLS, so only
# controls with identical request and response schemas round-trip.
# Returning raw tuples like the module-level API is a 4.0 question, #643.
def _test_controls(self, option):
Comment thread
mistotebe marked this conversation as resolved.
self._check_option(option, [])

@unittest.expectedFailure
def test_server_controls(self):
self._test_controls(ldap.OPT_SERVER_CONTROLS)
self.set_option(option, [
SimplePagedResultsControl(criticality=0, size=5, cookie=b'cookie'),
])
try:
paged, = self.get_option(option)
self.assertIsInstance(paged, SimplePagedResultsControl)
self.assertEqual(paged.criticality, 0)
self.assertEqual(paged.size, 5)
self.assertEqual(paged.cookie, b'cookie')
finally:
self.set_option(option, [])

# no request value, SEQUENCE response: critical raises,
# non-critical is dropped
self.set_option(option, [SearchNoOpControl(criticality=1)])
try:
with self.assertRaises(PyAsn1Error):
self.get_option(option)
finally:
self.set_option(option, [])
self.set_option(option, [SearchNoOpControl(criticality=0)])
try:
self.assertEqual(self.get_option(option), [])
finally:
self.set_option(option, [])

with self.assertRaises(TypeError):
self.set_option(option, object)
with self.assertRaises(TypeError):
# data must be bytes or None
self.set_option(option, [
RequestControl(TEST_CTRL[0][0], TEST_CTRL[0][1], 'data'),
])


if __name__ == '__main__':
Expand Down