summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrej Shadura <andrew.shadura@collabora.co.uk>2020-11-30 12:16:10 +0100
committerAndrej Shadura <andrew.shadura@collabora.co.uk>2020-11-30 12:16:10 +0100
commitd7ea1d4c872a0ef39cce7a6bb6d2b9c9fd6c205e (patch)
tree509fcc27647de80e229c24babd4685ba04b45802
parent2bcee5c91d65796532aef1b15f6c8c0c16197aee (diff)
parent96fb2df0e33b34a4dbb75577de8589a8611759c4 (diff)
Update upstream source from tag 'upstream/2.8.1'
Update to upstream version '2.8.1' with Debian dir 4d1ac29b7ae225ca99fb367a07e2e8183bc94036
-rw-r--r--PKG-INFO45
-rw-r--r--README.rst43
-rw-r--r--_version.json2
-rw-r--r--ldap3.egg-info/PKG-INFO45
-rw-r--r--ldap3.egg-info/SOURCES.txt1
-rw-r--r--ldap3.egg-info/requires.txt2
-rw-r--r--ldap3/__init__.py11
-rw-r--r--ldap3/abstract/cursor.py16
-rw-r--r--ldap3/abstract/entry.py39
-rw-r--r--ldap3/core/connection.py140
-rw-r--r--ldap3/core/exceptions.py8
-rw-r--r--ldap3/core/server.py45
-rw-r--r--ldap3/core/tls.py4
-rw-r--r--ldap3/extend/microsoft/addMembersToGroups.py15
-rw-r--r--ldap3/extend/microsoft/dirSync.py7
-rw-r--r--ldap3/extend/microsoft/modifyPassword.py5
-rw-r--r--ldap3/extend/microsoft/removeMembersFromGroups.py11
-rw-r--r--ldap3/extend/microsoft/unlockAccount.py9
-rw-r--r--ldap3/extend/novell/addMembersToGroups.py22
-rw-r--r--ldap3/extend/novell/checkGroupsMemberships.py12
-rw-r--r--ldap3/extend/novell/removeMembersFromGroups.py22
-rw-r--r--ldap3/extend/operation.py31
-rw-r--r--ldap3/extend/standard/PagedSearch.py9
-rw-r--r--ldap3/operation/search.py3
-rw-r--r--ldap3/protocol/formatters/validators.py6
-rw-r--r--ldap3/protocol/rfc2849.py7
-rw-r--r--ldap3/protocol/sasl/digestMd5.py4
-rw-r--r--ldap3/protocol/sasl/kerberos.py273
-rw-r--r--ldap3/strategy/base.py77
-rw-r--r--ldap3/strategy/restartable.py515
-rw-r--r--ldap3/strategy/safeSync.py32
-rw-r--r--ldap3/strategy/sync.py2
-rw-r--r--ldap3/utils/config.py9
-rw-r--r--ldap3/utils/conv.py4
-rw-r--r--ldap3/utils/ntlm.py10
-rw-r--r--ldap3/utils/port_validators.py2
-rw-r--r--ldap3/utils/uri.py236
-rw-r--r--ldap3/version.py8
-rw-r--r--requirements.txt2
-rw-r--r--test/testAbandonOperation.py14
-rw-r--r--test/testAddMembersToGroups.py50
-rw-r--r--test/testAddOperation.py12
-rw-r--r--test/testBytesOperation.py218
-rw-r--r--test/testCheckNamesFalse.py16
-rw-r--r--test/testCheckNamesTrue.py16
-rw-r--r--test/testCheckedAttributes.py16
-rw-r--r--test/testRemoveMembersFromGroups.py122
-rw-r--r--test/testRestartable.py4
-rw-r--r--test/testSearchAndModifyEntries.py20
-rw-r--r--test/testSearchOperation.py165
-rw-r--r--test/testSearchOperationEntries.py101
-rw-r--r--test/testSearchOperationJSON.py79
-rw-r--r--test/testTls.py14
-rw-r--r--test/testTransactions.py28
54 files changed, 1193 insertions, 1416 deletions
diff --git a/PKG-INFO b/PKG-INFO
index 6cabfde..2f587de 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: ldap3
-Version: 2.7
+Version: 2.8.1
Summary: A strictly RFC 4510 conforming LDAP V3 pure Python client library
Home-page: https://github.com/cannatag/ldap3
Author: Giovanni Cannata
@@ -25,15 +25,6 @@ Description: LDAP3
ldap3 is a strictly RFC 4510 conforming **LDAP V3 pure Python client** library. The same codebase runs in Python 2, Python 3, PyPy and PyPy3.
- Version 2 warning
- -----------------
-
- In version 2 of ldap3 some default values have been changed and the ldap3 namespace has been decluttered, removing redundant
- constants (look at the changelog for details). Also, the result code constants were moved to ldap3.core.results and the ldap3 custom exceptions
- were stored in ldap3.core.exceptions. If you experience errors in your existing code you should rearrange the import statements or explicitly
- set the defaults to their former values.
-
-
A more pythonic LDAP
--------------------
@@ -42,10 +33,34 @@ Description: LDAP3
interact with the LDAP server in a modern and *pythonic* way. With the Abstraction Layer you don't need to directly issue any LDAP operation at all.
+ Version 2.8 note
+ -----------------
+
+ Version 2.8 of ldap3 introduced **SafeSync**, a new connection strategy that can be used in multithreaded programs. In previous version only the ASYNC strategy was thread safe.
+ Each LDAP operation with the SafeSync strategy returns a tuple of four elements: status, result, response and request.
+
+ * status: states if the operation was successful
+
+ * result: the LDAP result of the operation
+
+ * response: the response of a LDAP Search Operation
+
+ * request: the original request of the operation
+
+ The SafeSync strategy can be used with the Abstract Layer, but the Abstract Layer currently is NOT thread safe.
+ The SafeSync import name is *SAFE_SYNC*::
+
+ from ldap3 import Server, Connection, SAFE_SYNC
+ server = Server('my_server')
+ conn = Connection(s, 'my_user', 'my_password', strategy=SAFE_SYNC, auto_bind=True)
+ status, result, response, _ = conn.search('o=test', '(objectclass=*)') # usually you don't need the original request (4th element of the return tuple)
+
+
+
Home Page
---------
- Project home page is https://github.com/cannatag/ldap3
+ The home page of the ldap3 project is https://github.com/cannatag/ldap3
Documentation
@@ -58,7 +73,7 @@ Description: LDAP3
-------
The ldap3 project is open source software released under the **LGPL v3 license**.
- Copyright 2013 - 2018 Giovanni Cannata
+ Copyright 2013 - 2020 Giovanni Cannata
PEP8 Compliance
@@ -91,8 +106,8 @@ Description: LDAP3
Continuous integration for testing is at https://travis-ci.org/cannatag/ldap3
- Support
- -------
+ Support & Development
+ ---------------------
You can submit support tickets on https://github.com/cannatag/ldap3/issues/new
You can submit pull request on the **dev** branch at https://github.com/cannatag/ldap3/tree/dev
@@ -121,7 +136,7 @@ Description: LDAP3
Donate
------
- If you want to keep this project up and running you can send me an Amazon gift card. I will use it to improve my skills in the Information and Communication technology.
+ If you want to keep this project up and running you can send me an Amazon gift card. I will use it to improve my skills in Information and Communication technologies.
Changelog
diff --git a/README.rst b/README.rst
index 78357df..49784ef 100644
--- a/README.rst
+++ b/README.rst
@@ -17,15 +17,6 @@ LDAP3
ldap3 is a strictly RFC 4510 conforming **LDAP V3 pure Python client** library. The same codebase runs in Python 2, Python 3, PyPy and PyPy3.
-Version 2 warning
------------------
-
-In version 2 of ldap3 some default values have been changed and the ldap3 namespace has been decluttered, removing redundant
-constants (look at the changelog for details). Also, the result code constants were moved to ldap3.core.results and the ldap3 custom exceptions
-were stored in ldap3.core.exceptions. If you experience errors in your existing code you should rearrange the import statements or explicitly
-set the defaults to their former values.
-
-
A more pythonic LDAP
--------------------
@@ -34,10 +25,34 @@ to not hog the server with heavy elaborations. To alleviate this ldap3 includes
interact with the LDAP server in a modern and *pythonic* way. With the Abstraction Layer you don't need to directly issue any LDAP operation at all.
+Version 2.8 note
+-----------------
+
+Version 2.8 of ldap3 introduced **SafeSync**, a new connection strategy that can be used in multithreaded programs. In previous version only the ASYNC strategy was thread safe.
+ Each LDAP operation with the SafeSync strategy returns a tuple of four elements: status, result, response and request.
+
+ * status: states if the operation was successful
+
+ * result: the LDAP result of the operation
+
+ * response: the response of a LDAP Search Operation
+
+ * request: the original request of the operation
+
+ The SafeSync strategy can be used with the Abstract Layer, but the Abstract Layer currently is NOT thread safe.
+ The SafeSync import name is *SAFE_SYNC*::
+
+ from ldap3 import Server, Connection, SAFE_SYNC
+ server = Server('my_server')
+ conn = Connection(s, 'my_user', 'my_password', strategy=SAFE_SYNC, auto_bind=True)
+ status, result, response, _ = conn.search('o=test', '(objectclass=*)') # usually you don't need the original request (4th element of the return tuple)
+
+
+
Home Page
---------
-Project home page is https://github.com/cannatag/ldap3
+The home page of the ldap3 project is https://github.com/cannatag/ldap3
Documentation
@@ -50,7 +65,7 @@ License
-------
The ldap3 project is open source software released under the **LGPL v3 license**.
-Copyright 2013 - 2018 Giovanni Cannata
+Copyright 2013 - 2020 Giovanni Cannata
PEP8 Compliance
@@ -83,8 +98,8 @@ Continuous integration
Continuous integration for testing is at https://travis-ci.org/cannatag/ldap3
-Support
--------
+Support & Development
+---------------------
You can submit support tickets on https://github.com/cannatag/ldap3/issues/new
You can submit pull request on the **dev** branch at https://github.com/cannatag/ldap3/tree/dev
@@ -113,7 +128,7 @@ For information and suggestions you can contact me at cannatag@gmail.com. You ca
Donate
------
-If you want to keep this project up and running you can send me an Amazon gift card. I will use it to improve my skills in the Information and Communication technology.
+If you want to keep this project up and running you can send me an Amazon gift card. I will use it to improve my skills in Information and Communication technologies.
Changelog
diff --git a/_version.json b/_version.json
index 6ae435c..0813ab8 100644
--- a/_version.json
+++ b/_version.json
@@ -6,6 +6,6 @@
"url": "https://github.com/cannatag/ldap3",
"description": "A strictly RFC 4510 conforming LDAP V3 pure Python client library",
"author": "Giovanni Cannata",
- "version": "2.7",
+ "version": "2.8.1",
"license": "LGPL v3"
}
diff --git a/ldap3.egg-info/PKG-INFO b/ldap3.egg-info/PKG-INFO
index 6cabfde..2f587de 100644
--- a/ldap3.egg-info/PKG-INFO
+++ b/ldap3.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: ldap3
-Version: 2.7
+Version: 2.8.1
Summary: A strictly RFC 4510 conforming LDAP V3 pure Python client library
Home-page: https://github.com/cannatag/ldap3
Author: Giovanni Cannata
@@ -25,15 +25,6 @@ Description: LDAP3
ldap3 is a strictly RFC 4510 conforming **LDAP V3 pure Python client** library. The same codebase runs in Python 2, Python 3, PyPy and PyPy3.
- Version 2 warning
- -----------------
-
- In version 2 of ldap3 some default values have been changed and the ldap3 namespace has been decluttered, removing redundant
- constants (look at the changelog for details). Also, the result code constants were moved to ldap3.core.results and the ldap3 custom exceptions
- were stored in ldap3.core.exceptions. If you experience errors in your existing code you should rearrange the import statements or explicitly
- set the defaults to their former values.
-
-
A more pythonic LDAP
--------------------
@@ -42,10 +33,34 @@ Description: LDAP3
interact with the LDAP server in a modern and *pythonic* way. With the Abstraction Layer you don't need to directly issue any LDAP operation at all.
+ Version 2.8 note
+ -----------------
+
+ Version 2.8 of ldap3 introduced **SafeSync**, a new connection strategy that can be used in multithreaded programs. In previous version only the ASYNC strategy was thread safe.
+ Each LDAP operation with the SafeSync strategy returns a tuple of four elements: status, result, response and request.
+
+ * status: states if the operation was successful
+
+ * result: the LDAP result of the operation
+
+ * response: the response of a LDAP Search Operation
+
+ * request: the original request of the operation
+
+ The SafeSync strategy can be used with the Abstract Layer, but the Abstract Layer currently is NOT thread safe.
+ The SafeSync import name is *SAFE_SYNC*::
+
+ from ldap3 import Server, Connection, SAFE_SYNC
+ server = Server('my_server')
+ conn = Connection(s, 'my_user', 'my_password', strategy=SAFE_SYNC, auto_bind=True)
+ status, result, response, _ = conn.search('o=test', '(objectclass=*)') # usually you don't need the original request (4th element of the return tuple)
+
+
+
Home Page
---------
- Project home page is https://github.com/cannatag/ldap3
+ The home page of the ldap3 project is https://github.com/cannatag/ldap3
Documentation
@@ -58,7 +73,7 @@ Description: LDAP3
-------
The ldap3 project is open source software released under the **LGPL v3 license**.
- Copyright 2013 - 2018 Giovanni Cannata
+ Copyright 2013 - 2020 Giovanni Cannata
PEP8 Compliance
@@ -91,8 +106,8 @@ Description: LDAP3
Continuous integration for testing is at https://travis-ci.org/cannatag/ldap3
- Support
- -------
+ Support & Development
+ ---------------------
You can submit support tickets on https://github.com/cannatag/ldap3/issues/new
You can submit pull request on the **dev** branch at https://github.com/cannatag/ldap3/tree/dev
@@ -121,7 +136,7 @@ Description: LDAP3
Donate
------
- If you want to keep this project up and running you can send me an Amazon gift card. I will use it to improve my skills in the Information and Communication technology.
+ If you want to keep this project up and running you can send me an Amazon gift card. I will use it to improve my skills in Information and Communication technologies.
Changelog
diff --git a/ldap3.egg-info/SOURCES.txt b/ldap3.egg-info/SOURCES.txt
index fb58879..6c70557 100644
--- a/ldap3.egg-info/SOURCES.txt
+++ b/ldap3.egg-info/SOURCES.txt
@@ -98,6 +98,7 @@ setup.py
./ldap3/strategy/mockSync.py
./ldap3/strategy/restartable.py
./ldap3/strategy/reusable.py
+./ldap3/strategy/safeSync.py
./ldap3/strategy/sync.py
./ldap3/utils/__init__.py
./ldap3/utils/asn1.py
diff --git a/ldap3.egg-info/requires.txt b/ldap3.egg-info/requires.txt
index 24395af..51f4785 100644
--- a/ldap3.egg-info/requires.txt
+++ b/ldap3.egg-info/requires.txt
@@ -1 +1 @@
-pyasn1>=0.1.8
+pyasn1>=0.4.6
diff --git a/ldap3/__init__.py b/ldap3/__init__.py
index 9bb5435..2a304ee 100644
--- a/ldap3/__init__.py
+++ b/ldap3/__init__.py
@@ -37,11 +37,11 @@ DIGEST_MD5 = 'DIGEST-MD5'
KERBEROS = GSSAPI = 'GSSAPI'
PLAIN = 'PLAIN'
-AUTO_BIND_DEFAULT = 'DEFAULT' # binds connection whens using "with" context manager
-AUTO_BIND_NONE = 'NONE' # same as False
-AUTO_BIND_NO_TLS = 'NO_TLS' # same as True
-AUTO_BIND_TLS_BEFORE_BIND = 'TLS_BEFORE_BIND'
-AUTO_BIND_TLS_AFTER_BIND = 'TLS_AFTER_BIND'
+AUTO_BIND_DEFAULT = 'DEFAULT' # binds connection when using "with" context manager
+AUTO_BIND_NONE = 'NONE' # same as False, no bind is performed
+AUTO_BIND_NO_TLS = 'NO_TLS' # same as True, bind is performed without tls
+AUTO_BIND_TLS_BEFORE_BIND = 'TLS_BEFORE_BIND' # start_tls is performed before bind
+AUTO_BIND_TLS_AFTER_BIND = 'TLS_AFTER_BIND' # start_tls is performed after bind
# server IP dual stack mode
IP_SYSTEM_DEFAULT = 'IP_SYSTEM_DEFAULT'
@@ -74,6 +74,7 @@ MODIFY_INCREMENT = 'MODIFY_INCREMENT'
# client strategies
SYNC = 'SYNC'
+SAFE_SYNC = 'SAFE_SYNC'
ASYNC = 'ASYNC'
LDIF = 'LDIF'
RESTARTABLE = 'RESTARTABLE'
diff --git a/ldap3/abstract/cursor.py b/ldap3/abstract/cursor.py
index 9259a2c..690e87d 100644
--- a/ldap3/abstract/cursor.py
+++ b/ldap3/abstract/cursor.py
@@ -349,8 +349,11 @@ class Cursor(object):
if not self.connection.strategy.sync:
response, result, request = self.connection.get_response(result, get_request=True)
else:
- response = self.connection.response
- result = self.connection.result
+ if self.connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ response = self.connection.response
+ result = self.connection.result
request = self.connection.request
self._store_operation_in_history(request, result, response)
@@ -820,9 +823,12 @@ class Writer(Cursor):
if not self.connection.strategy.sync:
response, result, request = self.connection.get_response(result, get_request=True)
else:
- response = self.connection.response
- result = self.connection.result
- request = self.connection.request
+ if self.connection.strategy.thread_safe:
+ _, result, response, request = result
+ else:
+ response = self.connection.response
+ result = self.connection.result
+ request = self.connection.request
if result['result'] in [RESULT_SUCCESS]:
break
diff --git a/ldap3/abstract/entry.py b/ldap3/abstract/entry.py
index b73c50f..2375796 100644
--- a/ldap3/abstract/entry.py
+++ b/ldap3/abstract/entry.py
@@ -31,6 +31,7 @@ except ImportError:
from ..utils.ordDict import OrderedDict # for Python 2.6
from os import linesep
+from copy import deepcopy
from .. import STRING_TYPES, SEQUENCE_TYPES, MODIFY_ADD, MODIFY_REPLACE
from .attribute import WritableAttribute
@@ -304,7 +305,7 @@ class EntryBase(object):
@property
def entry_attributes_as_dict(self):
- return dict((attribute_key, attribute_value.values) for (attribute_key, attribute_value) in self._state.attributes.items())
+ return dict((attribute_key, deepcopy(attribute_value.values)) for (attribute_key, attribute_value) in self._state.attributes.items())
@property
def entry_read_time(self):
@@ -535,9 +536,12 @@ class WritableEntry(EntryBase):
if not self.entry_cursor.connection.strategy.sync:
response, result, request = self.entry_cursor.connection.get_response(result, get_request=True)
else:
- response = self.entry_cursor.connection.response
- result = self.entry_cursor.connection.result
- request = self.entry_cursor.connection.request
+ if self.entry_cursor.connection.strategy.thread_safe:
+ _, result, response, request = result
+ else:
+ response = self.entry_cursor.connection.response
+ result = self.entry_cursor.connection.result
+ request = self.entry_cursor.connection.request
self.entry_cursor._store_operation_in_history(request, result, response)
if result['result'] == RESULT_SUCCESS:
dn = self.entry_dn
@@ -557,9 +561,12 @@ class WritableEntry(EntryBase):
if not self.entry_cursor.connection.strategy.sync:
response, result, request = self.entry_cursor.connection.get_response(result, get_request=True)
else:
- response = self.entry_cursor.connection.response
- result = self.entry_cursor.connection.result
- request = self.entry_cursor.connection.request
+ if self.entry_cursor.connection.strategy.thread_safe:
+ _, result, response, request = result
+ else:
+ response = self.entry_cursor.connection.response
+ result = self.entry_cursor.connection.result
+ request = self.entry_cursor.connection.request
self.entry_cursor._store_operation_in_history(request, result, response)
if result['result'] == RESULT_SUCCESS:
self._state.dn = safe_dn('+'.join(safe_rdn(self.entry_dn)) + ',' + self._state._to)
@@ -577,9 +584,12 @@ class WritableEntry(EntryBase):
if not self.entry_cursor.connection.strategy.sync:
response, result, request = self.entry_cursor.connection.get_response(result, get_request=True)
else:
- response = self.entry_cursor.connection.response
- result = self.entry_cursor.connection.result
- request = self.entry_cursor.connection.request
+ if self.entry_cursor.connection.strategy.thread_safe:
+ _, result, response, request = result
+ else:
+ response = self.entry_cursor.connection.response
+ result = self.entry_cursor.connection.result
+ request = self.entry_cursor.connection.request
self.entry_cursor._store_operation_in_history(request, result, response)
if result['result'] == RESULT_SUCCESS:
self._state.dn = rdn + ',' + ','.join(to_dn(self.entry_dn)[1:])
@@ -628,9 +638,12 @@ class WritableEntry(EntryBase):
if not self.entry_cursor.connection.strategy.sync: # asynchronous request
response, result, request = self.entry_cursor.connection.get_response(result, get_request=True)
else:
- response = self.entry_cursor.connection.response
- result = self.entry_cursor.connection.result
- request = self.entry_cursor.connection.request
+ if self.entry_cursor.connection.strategy.thread_safe:
+ _, result, response, request = result
+ else:
+ response = self.entry_cursor.connection.response
+ result = self.entry_cursor.connection.result
+ request = self.entry_cursor.connection.request
self.entry_cursor._store_operation_in_history(request, result, response)
if result['result'] == RESULT_SUCCESS:
diff --git a/ldap3/core/connection.py b/ldap3/core/connection.py
index 0f148e8..6d5b71b 100644
--- a/ldap3/core/connection.py
+++ b/ldap3/core/connection.py
@@ -22,7 +22,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with ldap3 in the COPYING and COPYING.LESSER files.
# If not, see <http://www.gnu.org/licenses/>.
-from copy import deepcopy
+from copy import deepcopy, copy
from os import linesep
from threading import RLock, Lock
from functools import reduce
@@ -30,9 +30,9 @@ import json
from .. import ANONYMOUS, SIMPLE, SASL, MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE, get_config_parameter, DEREF_ALWAYS, \
SUBTREE, ASYNC, SYNC, NO_ATTRIBUTES, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, MODIFY_INCREMENT, LDIF, ASYNC_STREAM, \
- RESTARTABLE, ROUND_ROBIN, REUSABLE, AUTO_BIND_DEFAULT, AUTO_BIND_NONE, AUTO_BIND_TLS_BEFORE_BIND,\
+ RESTARTABLE, ROUND_ROBIN, REUSABLE, AUTO_BIND_DEFAULT, AUTO_BIND_NONE, AUTO_BIND_TLS_BEFORE_BIND, SAFE_SYNC, \
AUTO_BIND_TLS_AFTER_BIND, AUTO_BIND_NO_TLS, STRING_TYPES, SEQUENCE_TYPES, MOCK_SYNC, MOCK_ASYNC, NTLM, EXTERNAL,\
- DIGEST_MD5, GSSAPI, PLAIN
+ DIGEST_MD5, GSSAPI, PLAIN, DSA, SCHEMA, ALL
from .results import RESULT_SUCCESS, RESULT_COMPARE_TRUE, RESULT_COMPARE_FALSE
from ..extend import ExtendedOperationsRoot
@@ -52,6 +52,7 @@ from ..protocol.sasl.digestMd5 import sasl_digest_md5
from ..protocol.sasl.external import sasl_external
from ..protocol.sasl.plain import sasl_plain
from ..strategy.sync import SyncStrategy
+from ..strategy.safeSync import SafeSyncStrategy
from ..strategy.mockAsync import MockAsyncStrategy
from ..strategy.asynchronous import AsyncStrategy
from ..strategy.reusable import ReusableStrategy
@@ -65,8 +66,7 @@ from .usage import ConnectionUsage
from .tls import Tls
from .exceptions import LDAPUnknownStrategyError, LDAPBindError, LDAPUnknownAuthenticationMethodError, \
LDAPSASLMechanismNotSupportedError, LDAPObjectClassError, LDAPConnectionIsReadOnlyError, LDAPChangeError, LDAPExceptionError, \
- LDAPObjectError, LDAPSocketReceiveError, LDAPAttributeError, LDAPInvalidValueError, LDAPConfigurationError, \
- LDAPInvalidPortError
+ LDAPObjectError, LDAPSocketReceiveError, LDAPAttributeError, LDAPInvalidValueError, LDAPInvalidPortError, LDAPStartTLSError
from ..utils.conv import escape_bytes, prepare_for_stream, check_json_dict, format_json, to_unicode
from ..utils.log import log, log_enabled, ERROR, BASIC, PROTOCOL, EXTENDED, get_library_log_hide_sensitive_data
@@ -80,6 +80,7 @@ SASL_AVAILABLE_MECHANISMS = [EXTERNAL,
PLAIN]
CLIENT_STRATEGIES = [SYNC,
+ SAFE_SYNC,
ASYNC,
LDIF,
RESTARTABLE,
@@ -116,7 +117,6 @@ def _format_socket_endpoints(sock):
return '<no socket>'
-# noinspection PyProtectedMember
class Connection(object):
"""Main ldap connection class.
@@ -181,7 +181,6 @@ class Connection(object):
:param source_port_list: a list of source ports to choose from when opening the connection to the server. Cannot be specified with source_port
:type source_port_list: list
"""
-
def __init__(self,
server,
user=None,
@@ -323,6 +322,8 @@ class Connection(object):
if self.strategy_type == SYNC:
self.strategy = SyncStrategy(self)
+ elif self.strategy_type == SAFE_SYNC:
+ self.strategy = SafeSyncStrategy(self)
elif self.strategy_type == ASYNC:
self.strategy = AsyncStrategy(self)
elif self.strategy_type == LDIF:
@@ -352,7 +353,7 @@ class Connection(object):
self.post_send_search = self.strategy.post_send_search
if not self.strategy.no_real_dsa:
- self.do_auto_bind()
+ self._do_auto_bind()
# else: # for strategies with a fake server set get_info to NONE if server hasn't a schema
# if self.server and not self.server.schema:
# self.server.get_info = NONE
@@ -362,7 +363,16 @@ class Connection(object):
else:
log(BASIC, 'instantiated Connection: <%r>', self)
- def do_auto_bind(self):
+ def _prepare_return_value(self, status, response=False):
+ if self.strategy.thread_safe:
+ temp_response = self.response
+ self.response = None
+ temp_request = self.request
+ self.request = None
+ return status, deepcopy(self.result), deepcopy(temp_response) if response else None, copy(temp_request)
+ return status
+
+ def _do_auto_bind(self):
if self.auto_bind and self.auto_bind not in [AUTO_BIND_NONE, AUTO_BIND_DEFAULT]:
if log_enabled(BASIC):
log(BASIC, 'performing automatic bind for <%s>', self)
@@ -371,17 +381,28 @@ class Connection(object):
if self.auto_bind == AUTO_BIND_NO_TLS:
self.bind(read_server_info=True)
elif self.auto_bind == AUTO_BIND_TLS_BEFORE_BIND:
- self.start_tls(read_server_info=False)
- self.bind(read_server_info=True)
+ if self.start_tls(read_server_info=False):
+ self.bind(read_server_info=True)
+ else:
+ error = 'automatic start_tls befored bind not successful' + (' - ' + self.last_error if self.last_error else '')
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', error, self)
+ self.unbind() # unbind anyway to close connection
+ raise LDAPStartTLSError(error)
elif self.auto_bind == AUTO_BIND_TLS_AFTER_BIND:
self.bind(read_server_info=False)
- self.start_tls(read_server_info=True)
+ if not self.start_tls(read_server_info=True):
+ error = 'automatic start_tls after bind not successful' + (' - ' + self.last_error if self.last_error else '')
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', error, self)
+ self.unbind()
+ raise LDAPStartTLSError(error)
if not self.bound:
- self.last_error = 'automatic bind not successful' + (' - ' + self.last_error if self.last_error else '')
+ error = 'automatic bind not successful' + (' - ' + self.last_error if self.last_error else '')
if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', self.last_error, self)
+ log(ERROR, '%s for <%s>', error, self)
self.unbind()
- raise LDAPBindError(self.last_error)
+ raise LDAPBindError(error)
def __str__(self):
s = [
@@ -509,11 +530,11 @@ class Connection(object):
if self.closed:
self.open()
if not self.bound:
- self.bind()
+ if not self.bind():
+ raise LDAPBindError('unable to bind')
return self
- # noinspection PyUnusedLocal
def __exit__(self, exc_type, exc_val, exc_tb):
with self.connection_lock:
context_bound, context_closed = self._context_state.pop()
@@ -646,7 +667,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done BIND operation, result <%s>', self.bound)
- return self.bound
+ return self._prepare_return_value(self.bound, self.result)
def rebind(self,
user=None,
@@ -689,10 +710,13 @@ class Connection(object):
try:
return self.bind(read_server_info, controls)
except LDAPSocketReceiveError:
- raise LDAPBindError('Unable to rebind as a different user, furthermore the server abruptly closed the connection')
+ self.last_error = 'Unable to rebind as a different user, furthermore the server abruptly closed the connection'
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', self.last_error, self)
+ raise LDAPBindError(self.last_error)
else:
self.strategy.pool.rebind_pool()
- return True
+ return self._prepare_return_value(True, self.result)
def unbind(self,
controls=None):
@@ -724,7 +748,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done UNBIND operation, result <%s>', True)
- return True
+ return self._prepare_return_value(True)
def search(self,
search_base,
@@ -799,7 +823,10 @@ class Connection(object):
else:
attribute_name_to_check = attribute_name
if self.server.schema and attribute_name_to_check.lower() not in conf_attributes_excluded_from_check and attribute_name_to_check not in self.server.schema.attribute_types:
- raise LDAPAttributeError('invalid attribute type ' + attribute_name_to_check)
+ self.last_error = 'invalid attribute type ' + attribute_name_to_check
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', self.last_error, self)
+ raise LDAPAttributeError(self.last_error)
request = search_operation(search_base,
search_filter,
@@ -838,7 +865,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done SEARCH operation, result <%s>', return_value)
- return return_value
+ return self._prepare_return_value(return_value, response=True)
def compare(self,
dn,
@@ -865,10 +892,16 @@ class Connection(object):
attribute_name_to_check = attribute
if self.server.schema.attribute_types and attribute_name_to_check.lower() not in conf_attributes_excluded_from_check and attribute_name_to_check not in self.server.schema.attribute_types:
- raise LDAPAttributeError('invalid attribute type ' + attribute_name_to_check)
+ self.last_error = 'invalid attribute type ' + attribute_name_to_check
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', self.last_error, self)
+ raise LDAPAttributeError(self.last_error)
if isinstance(value, SEQUENCE_TYPES): # value can't be a sequence
- raise LDAPInvalidValueError('value cannot be a sequence')
+ self.last_error = 'value cannot be a sequence'
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', self.last_error, self)
+ raise LDAPInvalidValueError(self.last_error)
with self.connection_lock:
self._fire_deferred()
@@ -892,7 +925,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done COMPARE operation, result <%s>', return_value)
- return return_value
+ return self._prepare_return_value(return_value)
def add(self,
dn,
@@ -950,7 +983,10 @@ class Connection(object):
if self.server and self.server.schema and self.check_names:
for object_class_name in _attributes[object_class_attr_name]:
if object_class_name.lower() not in conf_classes_excluded_from_check and object_class_name not in self.server.schema.object_classes:
- raise LDAPObjectClassError('invalid object class ' + str(object_class_name))
+ self.last_error = 'invalid object class ' + str(object_class_name)
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', self.last_error, self)
+ raise LDAPObjectClassError(self.last_error)
for attribute_name in _attributes:
if ';' in attribute_name: # remove tags for checking
@@ -959,7 +995,10 @@ class Connection(object):
attribute_name_to_check = attribute_name
if attribute_name_to_check.lower() not in conf_attributes_excluded_from_check and attribute_name_to_check not in self.server.schema.attribute_types:
- raise LDAPAttributeError('invalid attribute type ' + attribute_name_to_check)
+ self.last_error = 'invalid attribute type ' + attribute_name_to_check
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', self.last_error, self)
+ raise LDAPAttributeError(self.last_error)
request = add_operation(dn, _attributes, self.auto_encode, self.server.schema if self.server else None, validator=self.server.custom_validator if self.server else None, check_names=self.check_names)
if log_enabled(PROTOCOL):
@@ -981,7 +1020,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done ADD operation, result <%s>', return_value)
- return return_value
+ return self._prepare_return_value(return_value)
def delete(self,
dn,
@@ -1025,7 +1064,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done DELETE operation, result <%s>', return_value)
- return return_value
+ return self._prepare_return_value(return_value)
def modify(self,
dn,
@@ -1077,7 +1116,10 @@ class Connection(object):
attribute_name_to_check = attribute_name
if self.server.schema.attribute_types and attribute_name_to_check.lower() not in conf_attributes_excluded_from_check and attribute_name_to_check not in self.server.schema.attribute_types:
- raise LDAPAttributeError('invalid attribute type ' + attribute_name_to_check)
+ self.last_error = 'invalid attribute type ' + attribute_name_to_check
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', self.last_error, self)
+ raise LDAPAttributeError(self.last_error)
change = changes[attribute_name]
if isinstance(change, SEQUENCE_TYPES) and change[0] in [MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE, MODIFY_INCREMENT, 0, 1, 2, 3]:
if len(change) != 2:
@@ -1115,7 +1157,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done MODIFY operation, result <%s>', return_value)
- return return_value
+ return self._prepare_return_value(return_value)
def modify_dn(self,
dn,
@@ -1172,7 +1214,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done MODIFY DN operation, result <%s>', return_value)
- return return_value
+ return self._prepare_return_value(return_value)
def abandon(self,
message_id,
@@ -1205,7 +1247,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done ABANDON operation, result <%s>', return_value)
- return return_value
+ return self._prepare_return_value(return_value)
def extended(self,
request_name,
@@ -1239,15 +1281,16 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done EXTENDED operation, result <%s>', return_value)
- return return_value
+ return self._prepare_return_value(return_value, response=True)
def start_tls(self, read_server_info=True): # as per RFC4511. Removal of TLS is defined as MAY in RFC4511 so the client can't implement a generic stop_tls method0
-
if log_enabled(BASIC):
log(BASIC, 'start START TLS operation via <%s>', self)
with self.connection_lock:
return_value = False
+ self.result = None
+
if not self.server.tls:
self.server.tls = Tls()
@@ -1271,7 +1314,7 @@ class Connection(object):
if log_enabled(BASIC):
log(BASIC, 'done START TLS operation, result <%s>', return_value)
- return return_value
+ return self._prepare_return_value(return_value)
def do_sasl_bind(self,
controls):
@@ -1474,7 +1517,8 @@ class Connection(object):
target.writelines(self.response_to_json(raw=raw, indent=indent, sort=sort))
target.close()
- def _fire_deferred(self, read_info=True):
+ def _fire_deferred(self, read_info=None):
+ # if read_info is None reads the schema and server info if not present, if False doesn't read server info, if True reads always server info
with self.connection_lock:
if self.lazy and not self._executing_deferred:
self._executing_deferred = True
@@ -1485,10 +1529,15 @@ class Connection(object):
if self._deferred_open:
self.open(read_server_info=False)
if self._deferred_start_tls:
- self.start_tls(read_server_info=False)
+ if not self.start_tls(read_server_info=False):
+ error = 'deferred start_tls not successful' + (' - ' + self.last_error if self.last_error else '')
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', error, self)
+ self.unbind()
+ raise LDAPStartTLSError(error)
if self._deferred_bind:
self.bind(read_server_info=False, controls=self._bind_controls)
- if read_info:
+ if (read_info is None and (not self.server.info and self.server.get_info in [DSA, ALL]) or (not self.server.schema and self.server.get_info in [SCHEMA, ALL])) or read_info:
self.refresh_server_info()
except LDAPExceptionError as e:
if log_enabled(ERROR):
@@ -1501,10 +1550,10 @@ class Connection(object):
def entries(self):
if self.response:
if not self._entries:
- self._entries = self._get_entries(self.response)
+ self._entries = self._get_entries(self.response, self.request)
return self._entries
- def _get_entries(self, search_response):
+ def _get_entries(self, search_response, search_request):
with self.connection_lock:
from .. import ObjectDef, Reader
@@ -1529,7 +1578,7 @@ class Connection(object):
object_def += list(attr_set) # converts the set in a list to be added to the object definition
object_defs.append((attr_set,
object_def,
- Reader(self, object_def, self.request['base'], self.request['filter'], attributes=attr_set) if self.strategy.sync else Reader(self, object_def, '', '', attributes=attr_set))
+ Reader(self, object_def, search_request['base'], search_request['filter'], attributes=attr_set) if self.strategy.sync else Reader(self, object_def, '', '', attributes=attr_set))
) # objects_defs contains a tuple with the set, the ObjectDef and a cursor
entries = []
@@ -1542,8 +1591,9 @@ class Connection(object):
entries.append(entry)
break
else:
+ self.last_error = 'attribute set not found for ' + str(resp_attr_set)
if log_enabled(ERROR):
- log(ERROR, 'attribute set not found for %s in <%s>', resp_attr_set, self)
- raise LDAPObjectError('attribute set not found for ' + str(resp_attr_set))
+ log(ERROR, self.last_error, self)
+ raise LDAPObjectError(self.last_error)
return entries
diff --git a/ldap3/core/exceptions.py b/ldap3/core/exceptions.py
index 29aed26..42e895a 100644
--- a/ldap3/core/exceptions.py
+++ b/ldap3/core/exceptions.py
@@ -598,12 +598,8 @@ def communication_exception_factory(exc_to_raise, exc):
raise LDAPExceptionError('unable to generate exception type ' + str(exc_to_raise))
-def start_tls_exception_factory(exc_to_raise, exc):
+def start_tls_exception_factory(exc):
"""
Generates a new exception class of the requested type merged with the exception raised by the interpreter
"""
-
- if exc_to_raise.__name__ == 'LDAPStartTLSError':
- return type(exc_to_raise.__name__, (exc_to_raise, type(exc)), dict())
- else:
- raise LDAPExceptionError('unable to generate exception type ' + str(exc_to_raise))
+ return type(LDAPStartTLSError.__name__, (LDAPStartTLSError, type(exc)), dict())
diff --git a/ldap3/core/server.py b/ldap3/core/server.py
index 43189ef..41ff2af 100644
--- a/ldap3/core/server.py
+++ b/ldap3/core/server.py
@@ -419,11 +419,18 @@ class Server(object):
'+'], # requests all remaining attributes (other),
get_operational_attributes=True)
+ if connection.strategy.thread_safe:
+ status, result, response, _ = result
+ else:
+ status = result
+ result = connection.result
+ response = connection.response
+
with self.dit_lock:
- if isinstance(result, bool): # sync request
- self._dsa_info = DsaInfo(connection.response[0]['attributes'], connection.response[0]['raw_attributes']) if result else self._dsa_info
- elif result: # asynchronous request, must check if attributes in response
- results, _ = connection.get_response(result)
+ if connection.strategy.sync: # sync request
+ self._dsa_info = DsaInfo(response[0]['attributes'], response[0]['raw_attributes']) if status else self._dsa_info
+ elif status: # asynchronous request, must check if attributes in response
+ results, _ = connection.get_response(status)
if len(results) == 1 and 'attributes' in results[0] and 'raw_attributes' in results[0]:
self._dsa_info = DsaInfo(results[0]['attributes'], results[0]['raw_attributes'])
@@ -446,12 +453,18 @@ class Server(object):
schema_entry = self._dsa_info.schema_entry if self._dsa_info.schema_entry else None
else:
result = connection.search(entry, '(objectClass=*)', BASE, attributes=['subschemaSubentry'], get_operational_attributes=True)
- if isinstance(result, bool): # sync request
- if result and 'subschemaSubentry' in connection.response[0]['raw_attributes']:
- if len(connection.response[0]['raw_attributes']['subschemaSubentry']) > 0:
- schema_entry = connection.response[0]['raw_attributes']['subschemaSubentry'][0]
+ if connection.strategy.thread_safe:
+ status, result, response, _ = result
+ else:
+ status = result
+ result = connection.result
+ response = connection.response
+ if connection.strategy.sync: # sync request
+ if status and 'subschemaSubentry' in response[0]['raw_attributes']:
+ if len(response[0]['raw_attributes']['subschemaSubentry']) > 0:
+ schema_entry = response[0]['raw_attributes']['subschemaSubentry'][0]
else: # asynchronous request, must check if subschemaSubentry in attributes
- results, _ = connection.get_response(result)
+ results, _ = connection.get_response(status)
if len(results) == 1 and 'raw_attributes' in results[0] and 'subschemaSubentry' in results[0]['attributes']:
if len(results[0]['raw_attributes']['subschemaSubentry']) > 0:
schema_entry = results[0]['raw_attributes']['subschemaSubentry'][0]
@@ -475,13 +488,19 @@ class Server(object):
'*'], # requests all remaining attributes (other)
get_operational_attributes=True
)
+ if connection.strategy.thread_safe:
+ status, result, response, _ = result
+ else:
+ status = result
+ result = connection.result
+ response = connection.response
with self.dit_lock:
self._schema_info = None
- if result:
- if isinstance(result, bool): # sync request
- self._schema_info = SchemaInfo(schema_entry, connection.response[0]['attributes'], connection.response[0]['raw_attributes']) if result else None
+ if status:
+ if connection.strategy.sync: # sync request
+ self._schema_info = SchemaInfo(schema_entry, response[0]['attributes'], response[0]['raw_attributes'])
else: # asynchronous request, must check if attributes in response
- results, result = connection.get_response(result)
+ results, result = connection.get_response(status)
if len(results) == 1 and 'attributes' in results[0] and 'raw_attributes' in results[0]:
self._schema_info = SchemaInfo(schema_entry, results[0]['attributes'], results[0]['raw_attributes'])
if self._schema_info and not self._schema_info.is_valid(): # flaky servers can return an empty schema, checks if it is so and set schema to None
diff --git a/ldap3/core/tls.py b/ldap3/core/tls.py
index 1539b9f..ca8592b 100644
--- a/ldap3/core/tls.py
+++ b/ldap3/core/tls.py
@@ -23,7 +23,7 @@
# along with ldap3 in the COPYING and COPYING.LESSER files.
# If not, see <http://www.gnu.org/licenses/>.
-from .exceptions import LDAPSSLNotSupportedError, LDAPSSLConfigurationError, LDAPStartTLSError, LDAPCertificateError, start_tls_exception_factory
+from .exceptions import LDAPSSLNotSupportedError, LDAPSSLConfigurationError, LDAPCertificateError, start_tls_exception_factory, LDAPStartTLSError
from .. import SEQUENCE_TYPES
from ..utils.log import log, log_enabled, ERROR, BASIC, NETWORK
@@ -286,7 +286,7 @@ class Tls(object):
connection.last_error = 'wrap socket error: ' + str(e)
if log_enabled(ERROR):
log(ERROR, 'error <%s> wrapping socket for TLS in <%s>', connection.last_error, connection)
- raise start_tls_exception_factory(LDAPStartTLSError, e)(connection.last_error)
+ raise start_tls_exception_factory(e)(connection.last_error)
finally:
connection.starting_tls = False
diff --git a/ldap3/extend/microsoft/addMembersToGroups.py b/ldap3/extend/microsoft/addMembersToGroups.py
index eaf6cfd..fb6528a 100644
--- a/ldap3/extend/microsoft/addMembersToGroups.py
+++ b/ldap3/extend/microsoft/addMembersToGroups.py
@@ -57,13 +57,15 @@ def ad_add_members_to_groups(connection,
error = False
for group in groups_dn:
if fix: # checks for existance of group and for already assigned members
- result = connection.search(group, '(objectclass=*)', BASE, dereference_aliases=DEREF_NEVER,
- attributes=['member'])
-
+ result = connection.search(group, '(objectclass=*)', BASE, dereference_aliases=DEREF_NEVER, attributes=['member'])
if not connection.strategy.sync:
response, result = connection.get_response(result)
else:
- response, result = connection.response, connection.result
+ if connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ response = connection.response
+ result = connection.result
if not result['description'] == 'success':
raise LDAPInvalidDnError(group + ' not found')
@@ -82,7 +84,10 @@ def ad_add_members_to_groups(connection,
if not connection.strategy.sync:
_, result = connection.get_response(result)
else:
- result = connection.result
+ if connection.strategy.thread_safe:
+ _, result, _, _ = result
+ else:
+ result = connection.result
if result['description'] != 'success':
error = True
result_error_params = ['result', 'description', 'dn', 'message']
diff --git a/ldap3/extend/microsoft/dirSync.py b/ldap3/extend/microsoft/dirSync.py
index db403a1..e5dd8ae 100644
--- a/ldap3/extend/microsoft/dirSync.py
+++ b/ldap3/extend/microsoft/dirSync.py
@@ -77,8 +77,11 @@ class DirSync(object):
if not self.connection.strategy.sync:
response, result = self.connection.get_response(result)
else:
- response = self.connection.response
- result = self.connection.result
+ if self.connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ response = self.connection.response
+ result = self.connection.result
if result['description'] == 'success' and 'controls' in result and '1.2.840.113556.1.4.841' in result['controls']:
self.more_results = result['controls']['1.2.840.113556.1.4.841']['value']['more_results']
diff --git a/ldap3/extend/microsoft/modifyPassword.py b/ldap3/extend/microsoft/modifyPassword.py
index 0bf1c06..e304caf 100644
--- a/ldap3/extend/microsoft/modifyPassword.py
+++ b/ldap3/extend/microsoft/modifyPassword.py
@@ -56,7 +56,10 @@ def ad_modify_password(connection, user_dn, new_password, old_password, controls
if not connection.strategy.sync:
_, result = connection.get_response(result)
else:
- result = connection.result
+ if connection.strategy.thread_safe:
+ _, result, _, _ = result
+ else:
+ result = connection.result
# change successful, returns True
if result['result'] == RESULT_SUCCESS:
diff --git a/ldap3/extend/microsoft/removeMembersFromGroups.py b/ldap3/extend/microsoft/removeMembersFromGroups.py
index 0998713..86b799a 100644
--- a/ldap3/extend/microsoft/removeMembersFromGroups.py
+++ b/ldap3/extend/microsoft/removeMembersFromGroups.py
@@ -62,7 +62,11 @@ def ad_remove_members_from_groups(connection,
if not connection.strategy.sync:
response, result = connection.get_response(result)
else:
- response, result = connection.response, connection.result
+ if connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ response = connection.response
+ result = connection.result
if not result['description'] == 'success':
raise LDAPInvalidDnError(group + ' not found')
@@ -81,7 +85,10 @@ def ad_remove_members_from_groups(connection,
if not connection.strategy.sync:
_, result = connection.get_response(result)
else:
- result = connection.result
+ if connection.strategy.thread_safe:
+ _, result, _, _ = result
+ else:
+ result = connection.result
if result['description'] != 'success':
error = True
result_error_params = ['result', 'description', 'dn', 'message']
diff --git a/ldap3/extend/microsoft/unlockAccount.py b/ldap3/extend/microsoft/unlockAccount.py
index bc59b58..b1c65b8 100644
--- a/ldap3/extend/microsoft/unlockAccount.py
+++ b/ldap3/extend/microsoft/unlockAccount.py
@@ -33,14 +33,15 @@ from ...utils.dn import safe_dn
def ad_unlock_account(connection, user_dn, controls=None):
if connection.check_names:
user_dn = safe_dn(user_dn)
- result = connection.modify(user_dn,
- {'lockoutTime': [(MODIFY_REPLACE, ['0'])]},
- controls)
+ result = connection.modify(user_dn, {'lockoutTime': [(MODIFY_REPLACE, ['0'])]}, controls)
if not connection.strategy.sync:
_, result = connection.get_response(result)
else:
- result = connection.result
+ if connection.strategy.thread_safe:
+ _, result, _, _ = result
+ else:
+ result = connection.result
# change successful, returns True
if result['result'] == RESULT_SUCCESS:
diff --git a/ldap3/extend/novell/addMembersToGroups.py b/ldap3/extend/novell/addMembersToGroups.py
index d649dc8..b832b9a 100644
--- a/ldap3/extend/novell/addMembersToGroups.py
+++ b/ldap3/extend/novell/addMembersToGroups.py
@@ -74,7 +74,11 @@ def edir_add_members_to_groups(connection,
if not connection.strategy.sync:
response, result = connection.get_response(result)
else:
- response, result = connection.response, connection.result
+ if connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ result = connection.result
+ response = connection.response
if not result['description'] == 'success':
raise LDAPInvalidDnError(member + ' not found')
@@ -98,7 +102,10 @@ def edir_add_members_to_groups(connection,
if not connection.strategy.sync:
_, result = connection.get_response(result)
else:
- result = connection.result
+ if connection.strategy.thread_safe:
+ _, result, _, _ = result
+ else:
+ result = connection.result
if result['description'] != 'success':
error = True
break
@@ -111,7 +118,11 @@ def edir_add_members_to_groups(connection,
if not connection.strategy.sync:
response, result = connection.get_response(result)
else:
- response, result = connection.response, connection.result
+ if connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ result = connection.result
+ response = connection.response
if not result['description'] == 'success':
raise LDAPInvalidDnError(group + ' not found')
@@ -136,7 +147,10 @@ def edir_add_members_to_groups(connection,
if not connection.strategy.sync:
_, result = connection.get_response(result)
else:
- result = connection.result
+ if connection.strategy.thread_safe:
+ _, result, _, _ = result
+ else:
+ result = connection.result
if result['description'] != 'success':
error = True
break
diff --git a/ldap3/extend/novell/checkGroupsMemberships.py b/ldap3/extend/novell/checkGroupsMemberships.py
index c51dbf2..0f65b5d 100644
--- a/ldap3/extend/novell/checkGroupsMemberships.py
+++ b/ldap3/extend/novell/checkGroupsMemberships.py
@@ -55,7 +55,11 @@ def _check_members_have_memberships(connection,
if not connection.strategy.sync:
response, result = connection.get_response(result)
else:
- response, result = connection.response, connection.result
+ if connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ result = connection.result
+ response = connection.response
if not result['description'] == 'success': # member not found in DIT
raise LDAPInvalidDnError(member + ' not found')
@@ -99,7 +103,11 @@ def _check_groups_contain_members(connection,
if not connection.strategy.sync:
response, result = connection.get_response(result)
else:
- response, result = connection.response, connection.result
+ if connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ result = connection.result
+ response = connection.response
if not result['description'] == 'success':
raise LDAPInvalidDnError(group + ' not found')
diff --git a/ldap3/extend/novell/removeMembersFromGroups.py b/ldap3/extend/novell/removeMembersFromGroups.py
index c46c275..5736079 100644
--- a/ldap3/extend/novell/removeMembersFromGroups.py
+++ b/ldap3/extend/novell/removeMembersFromGroups.py
@@ -75,7 +75,11 @@ def edir_remove_members_from_groups(connection,
if not connection.strategy.sync:
response, result = connection.get_response(result)
else:
- response, result = connection.response, connection.result
+ if connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ response = connection.response
+ result = connection.result
if not result['description'] == 'success':
raise LDAPInvalidDnError(member + ' not found')
@@ -100,7 +104,10 @@ def edir_remove_members_from_groups(connection,
if not connection.strategy.sync:
_, result = connection.get_response(result)
else:
- result = connection.result
+ if connection.strategy.thread_safe:
+ _, result, _, _ = result
+ else:
+ result = connection.result
if result['description'] != 'success':
error = True
break
@@ -113,7 +120,11 @@ def edir_remove_members_from_groups(connection,
if not connection.strategy.sync:
response, result = connection.get_response(result)
else:
- response, result = connection.response, connection.result
+ if connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ response = connection.response
+ result = connection.result
if not result['description'] == 'success':
raise LDAPInvalidDnError(group + ' not found')
@@ -139,7 +150,10 @@ def edir_remove_members_from_groups(connection,
if not connection.strategy.sync:
_, result = connection.get_response(result)
else:
- result = connection.result
+ if connection.strategy.thread_safe:
+ _, result, _, _ = result
+ else:
+ result = connection.result
if result['description'] != 'success':
error = True
break
diff --git a/ldap3/extend/operation.py b/ldap3/extend/operation.py
index c1d478c..fcfdad4 100644
--- a/ldap3/extend/operation.py
+++ b/ldap3/extend/operation.py
@@ -52,10 +52,14 @@ class ExtendedOperation(object):
resp = self.connection.extended(self.request_name, self.request_value, self.controls)
if not self.connection.strategy.sync:
- _, self.result = self.connection.get_response(resp)
+ _, result = self.connection.get_response(resp)
else:
- self.result = self.connection.result
- self.decode_response()
+ if self.connection.strategy.thread_safe:
+ _, result, _, _ = resp
+ else:
+ result = self.connection.result
+ self.result = result
+ self.decode_response(result)
self.populate_result()
self.set_response()
return self.response_value
@@ -63,29 +67,32 @@ class ExtendedOperation(object):
def populate_result(self):
pass
- def decode_response(self):
- if not self.result:
+ def decode_response(self, response=None):
+ if not response:
+ response = self.result
+ if not response:
return None
- if self.result['result'] not in [RESULT_SUCCESS]:
+ if response['result'] not in [RESULT_SUCCESS]:
if self.connection.raise_exceptions:
- raise LDAPExtensionError('extended operation error: ' + self.result['description'] + ' - ' + self.result['message'])
+ raise LDAPExtensionError('extended operation error: ' + response['description'] + ' - ' + response['message'])
else:
return None
- if not self.response_name or self.result['responseName'] == self.response_name:
- if self.result['responseValue']:
+ if not self.response_name or response['responseName'] == self.response_name:
+ if response['responseValue']:
if self.asn1_spec is not None:
- decoded, unprocessed = decoder.decode(self.result['responseValue'], asn1Spec=self.asn1_spec)
+ decoded, unprocessed = decoder.decode(response['responseValue'], asn1Spec=self.asn1_spec)
if unprocessed:
raise LDAPExtensionError('error decoding extended response value')
self.decoded_response = decoded
else:
- self.decoded_response = self.result['responseValue']
+ self.decoded_response = response['responseValue']
else:
raise LDAPExtensionError('invalid response name received')
def set_response(self):
self.response_value = self.result[self.response_attribute] if self.result and self.response_attribute in self.result else None
- self.connection.response = self.response_value
+ if not self.connection.strategy.thread_safe:
+ self.connection.response = self.response_value
def config(self):
pass
diff --git a/ldap3/extend/standard/PagedSearch.py b/ldap3/extend/standard/PagedSearch.py
index f8bc7e6..a0f986e 100644
--- a/ldap3/extend/standard/PagedSearch.py
+++ b/ldap3/extend/standard/PagedSearch.py
@@ -67,11 +67,14 @@ def paged_search_generator(connection,
paged_criticality,
None if cookie is True else cookie)
- if not isinstance(result, bool):
+ if not connection.strategy.sync:
response, result = connection.get_response(result)
else:
- response = connection.response
- result = connection.result
+ if connection.strategy.thread_safe:
+ _, result, response, _ = result
+ else:
+ response = connection.response
+ result = connection.result
if result['referrals'] and original_auto_referrals: # if rererrals are returned start over the loop with a new connection to the referral
if not original_connection:
diff --git a/ldap3/operation/search.py b/ldap3/operation/search.py
index b78d86d..e12c13c 100644
--- a/ldap3/operation/search.py
+++ b/ldap3/operation/search.py
@@ -38,7 +38,6 @@ from ..operation.bind import referrals_to_list
from ..protocol.convert import ava_to_dict, attributes_to_list, search_refs_to_list, validate_assertion_value, prepare_filter_for_sending, search_refs_to_list_fast
from ..protocol.formatters.standard import format_attribute_values
from ..utils.conv import to_unicode, to_raw
-from pyasn1.error import PyAsn1UnicodeDecodeError
ROOT = 0
AND = 1
@@ -381,7 +380,7 @@ def search_operation(search_base,
def decode_vals(vals):
try:
return [str(val) for val in vals if val] if vals else None
- except PyAsn1UnicodeDecodeError:
+ except UnicodeDecodeError:
return decode_raw_vals(vals)
def decode_vals_fast(vals):
diff --git a/ldap3/protocol/formatters/validators.py b/ldap3/protocol/formatters/validators.py
index 3ab300d..bbc459f 100644
--- a/ldap3/protocol/formatters/validators.py
+++ b/ldap3/protocol/formatters/validators.py
@@ -368,7 +368,7 @@ def validate_uuid(input_value):
def validate_uuid_le(input_value):
- """
+ r"""
Active Directory stores objectGUID in uuid_le format, follows RFC4122 and MS-DTYP:
"{07039e68-4373-264d-a0a7-07039e684373}": string representation big endian, converted to little endian (with or without brace curles)
"689e030773434d26a7a007039e684373": packet representation, already in little endian
@@ -401,9 +401,7 @@ def validate_uuid_le(input_value):
error = True
elif '\\' in element:
try:
- uuid = UUID(bytes_le=ldap_escape_to_bytes(element)).bytes_le
- uuid = escape_bytes(uuid)
- valid_values.append(uuid) # byte representation, value in little endian
+ valid_values.append(UUID(bytes_le=ldap_escape_to_bytes(element)).bytes_le) # byte representation, value in little endian
changed = True
except ValueError:
error = True
diff --git a/ldap3/protocol/rfc2849.py b/ldap3/protocol/rfc2849.py
index 953be33..588af9e 100644
--- a/ldap3/protocol/rfc2849.py
+++ b/ldap3/protocol/rfc2849.py
@@ -30,10 +30,11 @@ from .. import STRING_TYPES
from ..core.exceptions import LDAPLDIFError, LDAPExtensionError
from ..protocol.persistentSearch import EntryChangeNotificationControl
from ..utils.asn1 import decoder
+from ..utils.config import get_config_parameter
# LDIF converter RFC 2849 compliant
-LDIF_LINE_LENGTH = 78
+conf_ldif_line_length = get_config_parameter('LDIF_LINE_LENGTH')
def safe_ldif_string(bytes_value):
@@ -233,8 +234,8 @@ def operation_to_ldif(operation_type, entries, all_base64=False, sort_order=None
# check max line length and split as per note 2 of RFC 2849
for line in lines:
if line:
- ldif_record.append(line[0:LDIF_LINE_LENGTH])
- ldif_record.extend([' ' + line[i: i + LDIF_LINE_LENGTH - 1] for i in range(LDIF_LINE_LENGTH, len(line), LDIF_LINE_LENGTH - 1)] if len(line) > LDIF_LINE_LENGTH else [])
+ ldif_record.append(line[0:conf_ldif_line_length])
+ ldif_record.extend([' ' + line[i: i + conf_ldif_line_length - 1] for i in range(conf_ldif_line_length, len(line), conf_ldif_line_length - 1)] if len(line) > conf_ldif_line_length else [])
else:
ldif_record.append('')
diff --git a/ldap3/protocol/sasl/digestMd5.py b/ldap3/protocol/sasl/digestMd5.py
index 48235d6..520be3c 100644
--- a/ldap3/protocol/sasl/digestMd5.py
+++ b/ldap3/protocol/sasl/digestMd5.py
@@ -94,7 +94,7 @@ def sasl_digest_md5(connection, controls):
authz_id = connection.sasl_credentials[3].encode(charset) if connection.sasl_credentials[3] else b''
nonce = server_directives['nonce'].encode(charset)
cnonce = random_hex_string(16).encode(charset)
- uri = b'ldap/'
+ uri = b'ldap/' + connection.server.host.encode(charset)
qop = b'auth'
digest_response = b'username="' + user + b'",'
@@ -110,7 +110,7 @@ def sasl_digest_md5(connection, controls):
a0 = md5_h(b':'.join([user, realm, password]))
a1 = b':'.join([a0, nonce, cnonce, authz_id]) if authz_id else b':'.join([a0, nonce, cnonce])
- a2 = b'AUTHENTICATE:' + uri + (':00000000000000000000000000000000' if qop in [b'auth-int', b'auth-conf'] else b'')
+ a2 = b'AUTHENTICATE:' + uri + (b':00000000000000000000000000000000' if qop in [b'auth-int', b'auth-conf'] else b'')
digest_response += b'response="' + md5_hex(md5_kd(md5_hex(md5_h(a1)), b':'.join([nonce, b'00000001', cnonce, qop, md5_hex(md5_h(a2))]))) + b'"'
diff --git a/ldap3/protocol/sasl/kerberos.py b/ldap3/protocol/sasl/kerberos.py
index 07db583..14a307e 100644
--- a/ldap3/protocol/sasl/kerberos.py
+++ b/ldap3/protocol/sasl/kerberos.py
@@ -1,123 +1,150 @@
-"""
-"""
-
-# Created on 2015.04.08
-#
-# Author: Giovanni Cannata
-#
-# Copyright 2015 - 2020 Giovanni Cannata
-#
-# This file is part of ldap3.
-#
-# ldap3 is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# ldap3 is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with ldap3 in the COPYING and COPYING.LESSER files.
-# If not, see <http://www.gnu.org/licenses/>.
-
-# original code by Hugh Cole-Baker, modified by Peter Foley
-# it needs the gssapi package
-import socket
-
-from ...core.exceptions import LDAPPackageUnavailableError, LDAPCommunicationError
-
-try:
- # noinspection PyPackageRequirements,PyUnresolvedReferences
- import gssapi
-except ImportError:
- raise LDAPPackageUnavailableError('package gssapi missing')
-
-from .sasl import send_sasl_negotiation, abort_sasl_negotiation
-
-NO_SECURITY_LAYER = 1
-INTEGRITY_PROTECTION = 2
-CONFIDENTIALITY_PROTECTION = 4
-
-
-def sasl_gssapi(connection, controls):
- """
- Performs a bind using the Kerberos v5 ("GSSAPI") SASL mechanism
- from RFC 4752. Does not support any security layers, only authentication!
-
- sasl_credentials can be empty or a tuple with one or two elements.
- The first element determines which service principal to request a ticket for and can be one of the following:
-
- - None or False, to use the hostname from the Server object
- - True to perform a reverse DNS lookup to retrieve the canonical hostname for the hosts IP address
- - A string containing the hostname
-
- The optional second element is what authorization ID to request.
-
- - If omitted or None, the authentication ID is used as the authorization ID
- - If a string, the authorization ID to use. Should start with "dn:" or "user:".
-
- The optional third element is a raw gssapi credentials structure which can be used over
- the implicit use of a krb ccache.
- """
- target_name = None
- authz_id = b""
- raw_creds = None
- creds = None
- if connection.sasl_credentials:
- if len(connection.sasl_credentials) >= 1 and connection.sasl_credentials[0]:
- if connection.sasl_credentials[0] is True:
- hostname = socket.gethostbyaddr(connection.socket.getpeername()[0])[0]
- target_name = gssapi.Name('ldap@' + hostname, gssapi.NameType.hostbased_service)
- else:
- target_name = gssapi.Name('ldap@' + connection.sasl_credentials[0], gssapi.NameType.hostbased_service)
- if len(connection.sasl_credentials) >= 2 and connection.sasl_credentials[1]:
- authz_id = connection.sasl_credentials[1].encode("utf-8")
- if len(connection.sasl_credentials) >= 3 and connection.sasl_credentials[2]:
- raw_creds = connection.sasl_credentials[2]
- if target_name is None:
- target_name = gssapi.Name('ldap@' + connection.server.host, gssapi.NameType.hostbased_service)
-
- if raw_creds is not None:
- creds = gssapi.Credentials(base=raw_creds, usage='initiate', store=connection.cred_store)
- else:
- creds = gssapi.Credentials(name=gssapi.Name(connection.user), usage='initiate', store=connection.cred_store) if connection.user else None
- ctx = gssapi.SecurityContext(name=target_name, mech=gssapi.MechType.kerberos, creds=creds)
- in_token = None
- try:
- while True:
- out_token = ctx.step(in_token)
- if out_token is None:
- out_token = ''
- result = send_sasl_negotiation(connection, controls, out_token)
- in_token = result['saslCreds']
- try:
- # This raised an exception in gssapi<1.1.2 if the context was
- # incomplete, but was fixed in
- # https://github.com/pythongssapi/python-gssapi/pull/70
- if ctx.complete:
- break
- except gssapi.exceptions.MissingContextError:
- pass
-
- unwrapped_token = ctx.unwrap(in_token)
- if len(unwrapped_token.message) != 4:
- raise LDAPCommunicationError("Incorrect response from server")
-
- server_security_layers = unwrapped_token.message[0]
- if not isinstance(server_security_layers, int):
- server_security_layers = ord(server_security_layers)
- if server_security_layers in (0, NO_SECURITY_LAYER):
- if unwrapped_token.message[1:] != '\x00\x00\x00':
- raise LDAPCommunicationError("Server max buffer size must be 0 if no security layer")
- if not (server_security_layers & NO_SECURITY_LAYER):
- raise LDAPCommunicationError("Server requires a security layer, but this is not implemented")
-
- client_security_layers = bytearray([NO_SECURITY_LAYER, 0, 0, 0])
- out_token = ctx.wrap(bytes(client_security_layers)+authz_id, False)
- return send_sasl_negotiation(connection, controls, out_token.message)
- except (gssapi.exceptions.GSSError, LDAPCommunicationError):
- abort_sasl_negotiation(connection, controls)
- raise
+"""
+"""
+
+# Created on 2015.04.08
+#
+# Author: Giovanni Cannata
+#
+# Copyright 2015 - 2020 Giovanni Cannata
+#
+# This file is part of ldap3.
+#
+# ldap3 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ldap3 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with ldap3 in the COPYING and COPYING.LESSER files.
+# If not, see <http://www.gnu.org/licenses/>.
+
+# original code by Hugh Cole-Baker, modified by Peter Foley
+# it needs the gssapi package
+import socket
+
+from ...core.exceptions import LDAPPackageUnavailableError, LDAPCommunicationError
+
+try:
+ # noinspection PyPackageRequirements,PyUnresolvedReferences
+ import gssapi
+ from gssapi.raw import ChannelBindings
+except ImportError:
+ raise LDAPPackageUnavailableError('package gssapi missing')
+
+from .sasl import send_sasl_negotiation, abort_sasl_negotiation
+
+
+NO_SECURITY_LAYER = 1
+INTEGRITY_PROTECTION = 2
+CONFIDENTIALITY_PROTECTION = 4
+
+
+def get_channel_bindings(ssl_socket):
+ try:
+ server_certificate = ssl_socket.getpeercert(True)
+ except:
+ # it is not SSL socket
+ return None
+ try:
+ from cryptography import x509
+ from cryptography.hazmat.backends import default_backend
+ from cryptography.hazmat.primitives import hashes
+ except ImportError:
+ raise LDAPPackageUnavailableError('package cryptography missing')
+ cert = x509.load_der_x509_certificate(server_certificate, default_backend())
+ hash_algorithm = cert.signature_hash_algorithm
+ # According to https://tools.ietf.org/html/rfc5929#section-4.1, we have to convert the the hash function for md5 and sha1
+ if hash_algorithm.name in ('md5', 'sha1'):
+ digest = hashes.Hash(hashes.SHA256(), default_backend())
+ else:
+ digest = hashes.Hash(hash_algorithm, default_backend())
+ digest.update(server_certificate)
+ application_data = b'tls-server-end-point:' + digest.finalize()
+ return ChannelBindings(application_data=application_data)
+
+
+def sasl_gssapi(connection, controls):
+ """
+ Performs a bind using the Kerberos v5 ("GSSAPI") SASL mechanism
+ from RFC 4752. Does not support any security layers, only authentication!
+
+ sasl_credentials can be empty or a tuple with one or two elements.
+ The first element determines which service principal to request a ticket for and can be one of the following:
+
+ - None or False, to use the hostname from the Server object
+ - True to perform a reverse DNS lookup to retrieve the canonical hostname for the hosts IP address
+ - A string containing the hostname
+
+ The optional second element is what authorization ID to request.
+
+ - If omitted or None, the authentication ID is used as the authorization ID
+ - If a string, the authorization ID to use. Should start with "dn:" or "user:".
+
+ The optional third element is a raw gssapi credentials structure which can be used over
+ the implicit use of a krb ccache.
+ """
+ target_name = None
+ authz_id = b""
+ raw_creds = None
+ creds = None
+ if connection.sasl_credentials:
+ if len(connection.sasl_credentials) >= 1 and connection.sasl_credentials[0]:
+ if connection.sasl_credentials[0] is True:
+ hostname = socket.gethostbyaddr(connection.socket.getpeername()[0])[0]
+ target_name = gssapi.Name('ldap@' + hostname, gssapi.NameType.hostbased_service)
+ else:
+ target_name = gssapi.Name('ldap@' + connection.sasl_credentials[0], gssapi.NameType.hostbased_service)
+ if len(connection.sasl_credentials) >= 2 and connection.sasl_credentials[1]:
+ authz_id = connection.sasl_credentials[1].encode("utf-8")
+ if len(connection.sasl_credentials) >= 3 and connection.sasl_credentials[2]:
+ raw_creds = connection.sasl_credentials[2]
+ if target_name is None:
+ target_name = gssapi.Name('ldap@' + connection.server.host, gssapi.NameType.hostbased_service)
+
+ if raw_creds is not None:
+ creds = gssapi.Credentials(base=raw_creds, usage='initiate', store=connection.cred_store)
+ else:
+ creds = gssapi.Credentials(name=gssapi.Name(connection.user), usage='initiate', store=connection.cred_store) if connection.user else None
+
+ ctx = gssapi.SecurityContext(name=target_name, mech=gssapi.MechType.kerberos, creds=creds, channel_bindings=get_channel_bindings(connection.socket))
+ in_token = None
+ try:
+ while True:
+ out_token = ctx.step(in_token)
+ if out_token is None:
+ out_token = ''
+ result = send_sasl_negotiation(connection, controls, out_token)
+ in_token = result['saslCreds']
+ try:
+ # This raised an exception in gssapi<1.1.2 if the context was
+ # incomplete, but was fixed in
+ # https://github.com/pythongssapi/python-gssapi/pull/70
+ if ctx.complete:
+ break
+ except gssapi.exceptions.MissingContextError:
+ pass
+
+ unwrapped_token = ctx.unwrap(in_token)
+ if len(unwrapped_token.message) != 4:
+ raise LDAPCommunicationError("Incorrect response from server")
+
+ server_security_layers = unwrapped_token.message[0]
+ if not isinstance(server_security_layers, int):
+ server_security_layers = ord(server_security_layers)
+ if server_security_layers in (0, NO_SECURITY_LAYER):
+ if unwrapped_token.message[1:] != '\x00\x00\x00':
+ raise LDAPCommunicationError("Server max buffer size must be 0 if no security layer")
+ if not (server_security_layers & NO_SECURITY_LAYER):
+ raise LDAPCommunicationError("Server requires a security layer, but this is not implemented")
+
+ client_security_layers = bytearray([NO_SECURITY_LAYER, 0, 0, 0])
+ out_token = ctx.wrap(bytes(client_security_layers)+authz_id, False)
+ return send_sasl_negotiation(connection, controls, out_token.message)
+ except (gssapi.exceptions.GSSError, LDAPCommunicationError):
+ abort_sasl_negotiation(connection, controls)
+ raise
diff --git a/ldap3/strategy/base.py b/ldap3/strategy/base.py
index 568459e..1acc732 100644
--- a/ldap3/strategy/base.py
+++ b/ldap3/strategy/base.py
@@ -24,15 +24,20 @@
# If not, see <http://www.gnu.org/licenses/>.
import socket
+try: # try to discover if unix sockets are available for LDAP over IPC (ldapi:// scheme)
+ # noinspection PyUnresolvedReferences
+ from socket import AF_UNIX
+ unix_socket_available = True
+except ImportError:
+ unix_socket_available = False
from struct import pack
from platform import system
-from time import sleep
from random import choice
from .. import SYNC, ANONYMOUS, get_config_parameter, BASE, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, NO_ATTRIBUTES
from ..core.results import DO_NOT_RAISE_EXCEPTIONS, RESULT_REFERRAL
from ..core.exceptions import LDAPOperationResult, LDAPSASLBindInProgressError, LDAPSocketOpenError, LDAPSessionTerminatedByServerError,\
- LDAPUnknownResponseError, LDAPUnknownRequestError, LDAPReferralError, communication_exception_factory, \
+ LDAPUnknownResponseError, LDAPUnknownRequestError, LDAPReferralError, communication_exception_factory, LDAPStartTLSError, \
LDAPSocketSendError, LDAPExceptionError, LDAPControlError, LDAPResponseTimeoutError, LDAPTransactionError
from ..utils.uri import parse_uri
from ..protocol.rfc4511 import LDAPMessage, ProtocolOp, MessageID, SearchResultEntry
@@ -78,6 +83,7 @@ class BaseStrategy(object):
self.pooled = None # Indicates a connection with a connection pool
self.can_stream = None # indicates if a strategy keeps a stream of responses (i.e. LdifProducer can accumulate responses with a single header). Stream must be initialized and closed in _start_listen() and _stop_listen()
self.referral_cache = {}
+ self.thread_safe = False # Indicates that connection can be used in a multithread application
if log_enabled(BASIC):
log(BASIC, 'instantiated <%s>: <%s>', self.__class__.__name__, self)
@@ -141,9 +147,6 @@ class BaseStrategy(object):
if log_enabled(ERROR):
log(ERROR, 'unable to open socket for <%s>', self.connection)
raise LDAPSocketOpenError('unable to open socket', exception_history)
- if log_enabled(ERROR):
- log(ERROR, 'unable to open socket for <%s>', self.connection)
- raise LDAPSocketOpenError('unable to open socket', exception_history)
elif not self.connection.server.current_address:
if log_enabled(ERROR):
log(ERROR, 'invalid server address for <%s>', self.connection)
@@ -151,7 +154,6 @@ class BaseStrategy(object):
self.connection._deferred_open = False
self._start_listen()
- # self.connection.do_auto_bind()
if log_enabled(NETWORK):
log(NETWORK, 'connection open for <%s>', self.connection)
@@ -199,7 +201,6 @@ class BaseStrategy(object):
log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
# raise communication_exception_factory(LDAPSocketOpenError, exc)(self.connection.last_error)
raise communication_exception_factory(LDAPSocketOpenError, type(e)(str(e)))(self.connection.last_error)
-
# Try to bind the socket locally before connecting to the remote address
# We go through our connection's source ports and try to bind our socket to our connection's source address
# with them.
@@ -210,25 +211,26 @@ class BaseStrategy(object):
# issue when no source address/port is specified if the system checking server availability is running
# as a very unprivileged user.
last_bind_exc = None
- socket_bind_succeeded = False
- for source_port in self.connection.source_port_list:
- try:
- self.connection.socket.bind((self.connection.source_address, source_port))
- socket_bind_succeeded = True
- break
- except Exception as bind_ex:
- last_bind_exc = bind_ex
- # we'll always end up logging at error level if we cannot bind any ports to the address locally.
- # but if some work and some don't you probably don't want the ones that don't at ERROR level
- if log_enabled(NETWORK):
- log(NETWORK, 'Unable to bind to local address <%s> with source port <%s> due to <%s>',
- self.connection.source_address, source_port, bind_ex)
- if not socket_bind_succeeded:
- self.connection.last_error = 'socket connection error while locally binding: ' + str(last_bind_exc)
- if log_enabled(ERROR):
- log(ERROR, 'Unable to locally bind to local address <%s> with any of the source ports <%s> for connection <%s due to <%s>',
- self.connection.source_address, self.connection.source_port_list, self.connection, last_bind_exc)
- raise communication_exception_factory(LDAPSocketOpenError, type(last_bind_exc)(str(last_bind_exc)))(last_bind_exc)
+ if unix_socket_available and self.connection.socket.family != socket.AF_UNIX:
+ socket_bind_succeeded = False
+ for source_port in self.connection.source_port_list:
+ try:
+ self.connection.socket.bind((self.connection.source_address, source_port))
+ socket_bind_succeeded = True
+ break
+ except Exception as bind_ex:
+ last_bind_exc = bind_ex
+ # we'll always end up logging at error level if we cannot bind any ports to the address locally.
+ # but if some work and some don't you probably don't want the ones that don't at ERROR level
+ if log_enabled(NETWORK):
+ log(NETWORK, 'Unable to bind to local address <%s> with source port <%s> due to <%s>',
+ self.connection.source_address, source_port, bind_ex)
+ if not socket_bind_succeeded:
+ self.connection.last_error = 'socket connection error while locally binding: ' + str(last_bind_exc)
+ if log_enabled(ERROR):
+ log(ERROR, 'Unable to locally bind to local address <%s> with any of the source ports <%s> for connection <%s due to <%s>',
+ self.connection.source_address, self.connection.source_port_list, self.connection, last_bind_exc)
+ raise communication_exception_factory(LDAPSocketOpenError, type(last_bind_exc)(str(last_bind_exc)))(last_bind_exc)
try: # set socket timeout for opening connection
if self.connection.server.connect_timeout:
@@ -273,7 +275,6 @@ class BaseStrategy(object):
self.connection.last_error = 'socket ssl wrapping error: ' + str(e)
if log_enabled(ERROR):
log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
- # raise communication_exception_factory(LDAPSocketOpenError, exc)(self.connection.last_error)
raise communication_exception_factory(LDAPSocketOpenError, type(e)(str(e)))(self.connection.last_error)
if self.connection.usage:
self.connection._usage.open_sockets += 1
@@ -693,13 +694,20 @@ class BaseStrategy(object):
search_scope=BASE,
dereference_aliases=request['dereferenceAlias'],
attributes=[attr_type + ';range=' + str(int(high_range) + 1) + '-*'])
- if isinstance(result, bool):
- if result:
- current_response = self.connection.response[0]
+ if self.connection.strategy.thread_safe:
+ status, result, _response, _ = result
+ else:
+ status = result
+ result = self.connection.result
+ _response = self.connection.response
+
+ if self.connection.strategy.sync:
+ if status:
+ current_response = _response[0]
else:
done = True
else:
- current_response, _ = self.get_response(result)
+ current_response, _ = self.get_response(status)
current_response = current_response[0]
if not done:
@@ -778,7 +786,12 @@ class BaseStrategy(object):
referral_connection.open()
referral_connection.strategy._referrals = self._referrals
if self.connection.tls_started and not referral_server.ssl: # if the original server was in start_tls mode and the referral server is not in ssl then start_tls on the referral connection
- referral_connection.start_tls()
+ if not referral_connection.start_tls():
+ error = 'start_tls in referral not successful' + (' - ' + referral_connection.last_error if referral_connection.last_error else '')
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', error, self)
+ self.unbind()
+ raise LDAPStartTLSError(error)
if self.connection.bound:
referral_connection.bind()
diff --git a/ldap3/strategy/restartable.py b/ldap3/strategy/restartable.py
index d739f41..bd689f9 100644
--- a/ldap3/strategy/restartable.py
+++ b/ldap3/strategy/restartable.py
@@ -1,255 +1,260 @@
-"""
-"""
-
-# Created on 2014.03.04
-#
-# Author: Giovanni Cannata
-#
-# Copyright 2014 - 2020 Giovanni Cannata
-#
-# This file is part of ldap3.
-#
-# ldap3 is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# ldap3 is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with ldap3 in the COPYING and COPYING.LESSER files.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from time import sleep
-import socket
-
-from .. import get_config_parameter
-from .sync import SyncStrategy
-from ..core.exceptions import LDAPSocketOpenError, LDAPOperationResult, LDAPMaximumRetriesError
-from ..utils.log import log, log_enabled, ERROR, BASIC
-
-
-# noinspection PyBroadException,PyProtectedMember
-class RestartableStrategy(SyncStrategy):
- def __init__(self, ldap_connection):
- SyncStrategy.__init__(self, ldap_connection)
- self.sync = True
- self.no_real_dsa = False
- self.pooled = False
- self.can_stream = False
- self.restartable_sleep_time = get_config_parameter('RESTARTABLE_SLEEPTIME')
- self.restartable_tries = get_config_parameter('RESTARTABLE_TRIES')
- self._restarting = False
- self._last_bind_controls = None
- self._current_message_type = None
- self._current_request = None
- self._current_controls = None
- self._restart_tls = None
- self.exception_history = []
-
- def open(self, reset_usage=False, read_server_info=True):
- SyncStrategy.open(self, reset_usage, read_server_info)
-
- def _open_socket(self, address, use_ssl=False, unix_socket=False):
- """
- Try to open and connect a socket to a Server
- raise LDAPExceptionError if unable to open or connect socket
- if connection is restartable tries for the number of restarting requested or forever
- """
- try:
- SyncStrategy._open_socket(self, address, use_ssl, unix_socket) # try to open socket using SyncWait
- self._reset_exception_history()
- return
- except Exception as e: # machinery for restartable connection
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
-
- if not self._restarting: # if not already performing a restart
- self._restarting = True
- counter = self.restartable_tries
- while counter > 0: # includes restartable_tries == True
- if log_enabled(BASIC):
- log(BASIC, 'try #%d to open Restartable connection <%s>', self.restartable_tries - counter, self.connection)
- sleep(self.restartable_sleep_time)
- if not self.connection.closed:
- try: # resetting connection
- self.connection.unbind()
- except (socket.error, LDAPSocketOpenError): # don't trace catch socket errors because socket could already be closed
- pass
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
- try: # reissuing same operation
- if self.connection.server_pool:
- new_server = self.connection.server_pool.get_server(self.connection) # get a server from the server_pool if available
- if self.connection.server != new_server:
- self.connection.server = new_server
- if self.connection.usage:
- self.connection._usage.servers_from_pool += 1
- SyncStrategy._open_socket(self, address, use_ssl, unix_socket) # calls super (not restartable) _open_socket()
- if self.connection.usage:
- self.connection._usage.restartable_successes += 1
- self.connection.closed = False
- self._restarting = False
- self._reset_exception_history()
- return
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
- if self.connection.usage:
- self.connection._usage.restartable_failures += 1
- if not isinstance(self.restartable_tries, bool):
- counter -= 1
- self._restarting = False
- self.connection.last_error = 'restartable connection strategy failed while opening socket'
- if log_enabled(ERROR):
- log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
- raise LDAPMaximumRetriesError(self.connection.last_error, self.exception_history, self.restartable_tries)
-
- def send(self, message_type, request, controls=None):
- self._current_message_type = message_type
- self._current_request = request
- self._current_controls = controls
- if not self._restart_tls: # RFCs doesn't define how to stop tls once started
- self._restart_tls = self.connection.tls_started
- if message_type == 'bindRequest': # stores controls used in bind operation to be used again when restarting the connection
- self._last_bind_controls = controls
-
- try:
- message_id = SyncStrategy.send(self, message_type, request, controls) # tries to send using SyncWait
- self._reset_exception_history()
- return message_id
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
- if not self._restarting: # machinery for restartable connection
- self._restarting = True
- counter = self.restartable_tries
- while counter > 0:
- if log_enabled(BASIC):
- log(BASIC, 'try #%d to send in Restartable connection <%s>', self.restartable_tries - counter, self.connection)
- sleep(self.restartable_sleep_time)
- if not self.connection.closed:
- try: # resetting connection
- self.connection.unbind()
- except (socket.error, LDAPSocketOpenError): # don't trace socket errors because socket could already be closed
- pass
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
- failure = False
- try: # reopening connection
- self.connection.open(reset_usage=False, read_server_info=False)
- if self._restart_tls: # restart tls if start_tls was previously used
- self.connection.start_tls(read_server_info=False)
- if message_type != 'bindRequest':
- self.connection.bind(read_server_info=False, controls=self._last_bind_controls) # binds with previously used controls unless the request is already a bindRequest
- if not self.connection.server.schema and not self.connection.server.info:
- self.connection.refresh_server_info()
- else:
- self.connection._fire_deferred(read_info=False) # in case of lazy connection, not open by the refresh_server_info
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
- failure = True
-
- if not failure:
- try: # reissuing same operation
- ret_value = self.connection.send(message_type, request, controls)
- if self.connection.usage:
- self.connection._usage.restartable_successes += 1
- self._restarting = False
- self._reset_exception_history()
- return ret_value # successful send
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
- failure = True
-
- if failure and self.connection.usage:
- self.connection._usage.restartable_failures += 1
-
- if not isinstance(self.restartable_tries, bool):
- counter -= 1
-
- self._restarting = False
-
- self.connection.last_error = 'restartable connection failed to send'
- if log_enabled(ERROR):
- log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
- raise LDAPMaximumRetriesError(self.connection.last_error, self.exception_history, self.restartable_tries)
-
- def post_send_single_response(self, message_id):
- try:
- ret_value = SyncStrategy.post_send_single_response(self, message_id)
- self._reset_exception_history()
- return ret_value
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
-
- # if an LDAPExceptionError is raised then resend the request
- try:
- ret_value = SyncStrategy.post_send_single_response(self, self.send(self._current_message_type, self._current_request, self._current_controls))
- self._reset_exception_history()
- return ret_value
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
- if not isinstance(e, LDAPOperationResult):
- self.connection.last_error = 'restartable connection strategy failed in post_send_single_response'
- if log_enabled(ERROR):
- log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
- raise
-
- def post_send_search(self, message_id):
- try:
- ret_value = SyncStrategy.post_send_search(self, message_id)
- self._reset_exception_history()
- return ret_value
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
-
- # if an LDAPExceptionError is raised then resend the request
- try:
- ret_value = SyncStrategy.post_send_search(self, self.connection.send(self._current_message_type, self._current_request, self._current_controls))
- self._reset_exception_history()
- return ret_value
- except Exception as e:
- if log_enabled(ERROR):
- log(ERROR, '<%s> while restarting <%s>', e, self.connection)
- self._add_exception_to_history(type(e)(str(e)))
- if not isinstance(e, LDAPOperationResult):
- self.connection.last_error = e.args
- if log_enabled(ERROR):
- log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
- raise e
-
- def _add_exception_to_history(self, exc):
- if not isinstance(self.restartable_tries, bool): # doesn't accumulate when restarting forever
- if not isinstance(exc, LDAPMaximumRetriesError): # doesn't add the LDAPMaximumRetriesError exception
- self.exception_history.append(exc)
-
- def _reset_exception_history(self):
- if self.exception_history:
- self.exception_history = []
-
- def get_stream(self):
- raise NotImplementedError
-
- def set_stream(self, value):
- raise NotImplementedError
+"""
+"""
+
+# Created on 2014.03.04
+#
+# Author: Giovanni Cannata
+#
+# Copyright 2014 - 2020 Giovanni Cannata
+#
+# This file is part of ldap3.
+#
+# ldap3 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ldap3 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with ldap3 in the COPYING and COPYING.LESSER files.
+# If not, see <http://www.gnu.org/licenses/>.
+
+from time import sleep
+import socket
+
+from .. import get_config_parameter
+from .sync import SyncStrategy
+from ..core.exceptions import LDAPSocketOpenError, LDAPOperationResult, LDAPMaximumRetriesError, LDAPStartTLSError
+from ..utils.log import log, log_enabled, ERROR, BASIC
+
+
+# noinspection PyBroadException,PyProtectedMember
+class RestartableStrategy(SyncStrategy):
+ def __init__(self, ldap_connection):
+ SyncStrategy.__init__(self, ldap_connection)
+ self.sync = True
+ self.no_real_dsa = False
+ self.pooled = False
+ self.can_stream = False
+ self.restartable_sleep_time = get_config_parameter('RESTARTABLE_SLEEPTIME')
+ self.restartable_tries = get_config_parameter('RESTARTABLE_TRIES')
+ self._restarting = False
+ self._last_bind_controls = None
+ self._current_message_type = None
+ self._current_request = None
+ self._current_controls = None
+ self._restart_tls = None
+ self.exception_history = []
+
+ def open(self, reset_usage=False, read_server_info=True):
+ SyncStrategy.open(self, reset_usage, read_server_info)
+
+ def _open_socket(self, address, use_ssl=False, unix_socket=False):
+ """
+ Try to open and connect a socket to a Server
+ raise LDAPExceptionError if unable to open or connect socket
+ if connection is restartable tries for the number of restarting requested or forever
+ """
+ try:
+ SyncStrategy._open_socket(self, address, use_ssl, unix_socket) # try to open socket using SyncWait
+ self._reset_exception_history()
+ return
+ except Exception as e: # machinery for restartable connection
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+
+ if not self._restarting: # if not already performing a restart
+ self._restarting = True
+ counter = self.restartable_tries
+ while counter > 0: # includes restartable_tries == True
+ if log_enabled(BASIC):
+ log(BASIC, 'try #%d to open Restartable connection <%s>', self.restartable_tries - counter, self.connection)
+ sleep(self.restartable_sleep_time)
+ if not self.connection.closed:
+ try: # resetting connection
+ self.connection.unbind()
+ except (socket.error, LDAPSocketOpenError): # don't trace catch socket errors because socket could already be closed
+ pass
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+ try: # reissuing same operation
+ if self.connection.server_pool:
+ new_server = self.connection.server_pool.get_server(self.connection) # get a server from the server_pool if available
+ if self.connection.server != new_server:
+ self.connection.server = new_server
+ if self.connection.usage:
+ self.connection._usage.servers_from_pool += 1
+ SyncStrategy._open_socket(self, address, use_ssl, unix_socket) # calls super (not restartable) _open_socket()
+ if self.connection.usage:
+ self.connection._usage.restartable_successes += 1
+ self.connection.closed = False
+ self._restarting = False
+ self._reset_exception_history()
+ return
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+ if self.connection.usage:
+ self.connection._usage.restartable_failures += 1
+ if not isinstance(self.restartable_tries, bool):
+ counter -= 1
+ self._restarting = False
+ self.connection.last_error = 'restartable connection strategy failed while opening socket'
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
+ raise LDAPMaximumRetriesError(self.connection.last_error, self.exception_history, self.restartable_tries)
+
+ def send(self, message_type, request, controls=None):
+ self._current_message_type = message_type
+ self._current_request = request
+ self._current_controls = controls
+ if not self._restart_tls: # RFCs doesn't define how to stop tls once started
+ self._restart_tls = self.connection.tls_started
+ if message_type == 'bindRequest': # stores controls used in bind operation to be used again when restarting the connection
+ self._last_bind_controls = controls
+
+ try:
+ message_id = SyncStrategy.send(self, message_type, request, controls) # tries to send using SyncWait
+ self._reset_exception_history()
+ return message_id
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+ if not self._restarting: # machinery for restartable connection
+ self._restarting = True
+ counter = self.restartable_tries
+ while counter > 0:
+ if log_enabled(BASIC):
+ log(BASIC, 'try #%d to send in Restartable connection <%s>', self.restartable_tries - counter, self.connection)
+ sleep(self.restartable_sleep_time)
+ if not self.connection.closed:
+ try: # resetting connection
+ self.connection.unbind()
+ except (socket.error, LDAPSocketOpenError): # don't trace socket errors because socket could already be closed
+ pass
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+ failure = False
+ try: # reopening connection
+ self.connection.open(reset_usage=False, read_server_info=False)
+ if self._restart_tls: # restart tls if start_tls was previously used
+ if self.connection.start_tls(read_server_info=False):
+ error = 'restart tls in restartable not successful' + (' - ' + self.connection.last_error if self.connection.last_error else '')
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', error, self)
+ self.connection.unbind()
+ raise LDAPStartTLSError(error)
+ if message_type != 'bindRequest':
+ self.connection.bind(read_server_info=False, controls=self._last_bind_controls) # binds with previously used controls unless the request is already a bindRequest
+ if not self.connection.server.schema and not self.connection.server.info:
+ self.connection.refresh_server_info()
+ else:
+ self.connection._fire_deferred(read_info=False) # in case of lazy connection, not open by the refresh_server_info
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+ failure = True
+
+ if not failure:
+ try: # reissuing same operation
+ ret_value = self.connection.send(message_type, request, controls)
+ if self.connection.usage:
+ self.connection._usage.restartable_successes += 1
+ self._restarting = False
+ self._reset_exception_history()
+ return ret_value # successful send
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+ failure = True
+
+ if failure and self.connection.usage:
+ self.connection._usage.restartable_failures += 1
+
+ if not isinstance(self.restartable_tries, bool):
+ counter -= 1
+
+ self._restarting = False
+
+ self.connection.last_error = 'restartable connection failed to send'
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
+ raise LDAPMaximumRetriesError(self.connection.last_error, self.exception_history, self.restartable_tries)
+
+ def post_send_single_response(self, message_id):
+ try:
+ ret_value = SyncStrategy.post_send_single_response(self, message_id)
+ self._reset_exception_history()
+ return ret_value
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+
+ # if an LDAPExceptionError is raised then resend the request
+ try:
+ ret_value = SyncStrategy.post_send_single_response(self, self.send(self._current_message_type, self._current_request, self._current_controls))
+ self._reset_exception_history()
+ return ret_value
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+ if not isinstance(e, LDAPOperationResult):
+ self.connection.last_error = 'restartable connection strategy failed in post_send_single_response'
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
+ raise
+
+ def post_send_search(self, message_id):
+ try:
+ ret_value = SyncStrategy.post_send_search(self, message_id)
+ self._reset_exception_history()
+ return ret_value
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+
+ # if an LDAPExceptionError is raised then resend the request
+ try:
+ ret_value = SyncStrategy.post_send_search(self, self.connection.send(self._current_message_type, self._current_request, self._current_controls))
+ self._reset_exception_history()
+ return ret_value
+ except Exception as e:
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> while restarting <%s>', e, self.connection)
+ self._add_exception_to_history(type(e)(str(e)))
+ if not isinstance(e, LDAPOperationResult):
+ self.connection.last_error = e.args
+ if log_enabled(ERROR):
+ log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
+ raise e
+
+ def _add_exception_to_history(self, exc):
+ if not isinstance(self.restartable_tries, bool): # doesn't accumulate when restarting forever
+ if not isinstance(exc, LDAPMaximumRetriesError): # doesn't add the LDAPMaximumRetriesError exception
+ self.exception_history.append(exc)
+
+ def _reset_exception_history(self):
+ if self.exception_history:
+ self.exception_history = []
+
+ def get_stream(self):
+ raise NotImplementedError
+
+ def set_stream(self, value):
+ raise NotImplementedError
diff --git a/ldap3/strategy/safeSync.py b/ldap3/strategy/safeSync.py
new file mode 100644
index 0000000..dcd2b92
--- /dev/null
+++ b/ldap3/strategy/safeSync.py
@@ -0,0 +1,32 @@
+"""
+"""
+
+# Created on 2020.07.12
+#
+# Author: Giovanni Cannata
+#
+# Copyright 2013 - 2020 Giovanni Cannata
+#
+# This file is part of ldap3.
+#
+# ldap3 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ldap3 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with ldap3 in the COPYING and COPYING.LESSER files.
+# If not, see <http://www.gnu.org/licenses/>.
+
+from .sync import SyncStrategy
+
+
+class SafeSyncStrategy(SyncStrategy):
+ def __init__(self, ldap_connection):
+ SyncStrategy.__init__(self, ldap_connection)
+ self.thread_safe = True
diff --git a/ldap3/strategy/sync.py b/ldap3/strategy/sync.py
index fdb1441..19883a6 100644
--- a/ldap3/strategy/sync.py
+++ b/ldap3/strategy/sync.py
@@ -54,7 +54,7 @@ class SyncStrategy(BaseStrategy):
def open(self, reset_usage=True, read_server_info=True):
BaseStrategy.open(self, reset_usage, read_server_info)
- if read_server_info:
+ if read_server_info and not self.connection._deferred_open:
try:
self.connection.refresh_server_info()
except LDAPOperationResult: # catch errors from server if raise_exception = True
diff --git a/ldap3/utils/config.py b/ldap3/utils/config.py
index e3edbf8..6e3aa97 100644
--- a/ldap3/utils/config.py
+++ b/ldap3/utils/config.py
@@ -91,6 +91,7 @@ _ADDITIONAL_SERVER_ENCODINGS = ['latin-1', 'koi8-r'] # some broken LDAP impleme
_ADDITIONAL_CLIENT_ENCODINGS = ['utf-8']
_IGNORE_MALFORMED_SCHEMA = False # some flaky LDAP servers returns malformed schema. If True no expection is raised and schema is thrown away
_DEFAULT_SERVER_ENCODING = 'utf-8' # should always be utf-8
+_LDIF_LINE_LENGTH = 78 # as stated in RFC 2849
if stdin and hasattr(stdin, 'encoding') and stdin.encoding:
_DEFAULT_CLIENT_ENCODING = stdin.encoding
@@ -124,7 +125,8 @@ PARAMETERS = ['CASE_INSENSITIVE_ATTRIBUTE_NAMES',
'ADDITIONAL_CLIENT_ENCODINGS',
'IGNORE_MALFORMED_SCHEMA',
'ATTRIBUTES_EXCLUDED_FROM_OBJECT_DEF',
- 'IGNORED_MANDATORY_ATTRIBUTES_IN_OBJECT_DEF'
+ 'IGNORED_MANDATORY_ATTRIBUTES_IN_OBJECT_DEF',
+ 'LDIF_LINE_LENGTH'
]
@@ -205,6 +207,8 @@ def get_config_parameter(parameter):
return _IGNORED_MANDATORY_ATTRIBUTES_IN_OBJECT_DEF
else:
return [_IGNORED_MANDATORY_ATTRIBUTES_IN_OBJECT_DEF]
+ elif parameter == 'LDIF_LINE_LENGTH': # Integer
+ return _LDIF_LINE_LENGTH
raise LDAPConfigurationParameterError('configuration parameter %s not valid' % parameter)
@@ -288,5 +292,8 @@ def set_config_parameter(parameter, value):
elif parameter == 'IGNORED_MANDATORY_ATTRIBUTES_IN_OBJECT_DEF':
global _IGNORED_MANDATORY_ATTRIBUTES_IN_OBJECT_DEF
_IGNORED_MANDATORY_ATTRIBUTES_IN_OBJECT_DEF = value
+ elif parameter == 'LDIF_LINE_LENGTH':
+ global _LDIF_LINE_LENGTH
+ _LDIF_LINE_LENGTH = value
else:
raise LDAPConfigurationParameterError('unable to set configuration parameter %s' % parameter)
diff --git a/ldap3/utils/conv.py b/ldap3/utils/conv.py
index b000e30..514faad 100644
--- a/ldap3/utils/conv.py
+++ b/ldap3/utils/conv.py
@@ -183,7 +183,7 @@ def json_hook(obj):
# noinspection PyProtectedMember
-def format_json(obj):
+def format_json(obj, iso_format=False):
if isinstance(obj, CaseInsensitiveDict):
return obj._store
@@ -194,6 +194,8 @@ def format_json(obj):
return obj
if isinstance(obj, datetime.timedelta):
+ if iso_format:
+ return obj.isoformat()
return str(obj)
if str is bytes: # Python 2
diff --git a/ldap3/utils/ntlm.py b/ldap3/utils/ntlm.py
index f91776d..5271669 100644
--- a/ldap3/utils/ntlm.py
+++ b/ldap3/utils/ntlm.py
@@ -493,5 +493,13 @@ class NtlmClient(object):
# The specified password is an LM:NTLM hash
password_digest = binascii.unhexlify(passparts[1])
else:
- password_digest = hashlib.new('MD4', self._password.encode('utf-16-le')).digest()
+ try:
+ password_digest = hashlib.new('MD4', self._password.encode('utf-16-le')).digest()
+ except ValueError as e:
+ try:
+ from Crypto.Hash import MD4 # try with the Crypto library if present
+ password_digest = MD4.new(self._password.encode('utf-16-le')).digest()
+ except ImportError:
+ raise e # raise original exception
+
return hmac.new(password_digest, (self.user_name.upper() + self.user_domain).encode('utf-16-le'), digestmod=hashlib.md5).digest()
diff --git a/ldap3/utils/port_validators.py b/ldap3/utils/port_validators.py
index a35e13e..c46b752 100644
--- a/ldap3/utils/port_validators.py
+++ b/ldap3/utils/port_validators.py
@@ -17,7 +17,7 @@ def check_port_and_port_list(port, port_list):
Return an error message indicating what is invalid if something isn't valid.
"""
if port is not None and port_list is not None:
- return'Cannot specify both a source port and a source port list'
+ return 'Cannot specify both a source port and a source port list'
elif port is not None:
if isinstance(port, int):
if port not in range(0, 65535):
diff --git a/ldap3/utils/uri.py b/ldap3/utils/uri.py
index 02c8e5a..3e56674 100644
--- a/ldap3/utils/uri.py
+++ b/ldap3/utils/uri.py
@@ -1,118 +1,118 @@
-"""
-"""
-
-# Created on 2014.09.08
-#
-# Author: Giovanni Cannata
-#
-# Copyright 2014 - 2020 Giovanni Cannata
-#
-# This file is part of ldap3.
-#
-# ldap3 is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# ldap3 is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with ldap3 in the COPYING and COPYING.LESSER files.
-# If not, see <http://www.gnu.org/licenses/>.
-
-try:
- from urllib.parse import unquote # Python3
-except ImportError:
- from urllib import unquote # Python 2
-
-from .. import SUBTREE, BASE, LEVEL
-
-
-def parse_uri(uri):
- """
- Decode LDAP URI as specified in RFC 4516 relaxing specifications
- permitting 'ldaps' as scheme for ssl-ldap
- """
-
- # ldapurl = scheme COLON SLASH SLASH [host [COLON port]]
- # [SLASH dn [QUESTION [attributes]
- # [QUESTION [scope] [QUESTION [filter]
- # [QUESTION extensions]]]]]
- # ; <host> and <port> are defined
- # ; in Sections 3.2.2 and 3.2.3
- # ; of [RFC3986].
- # ; <filter> is from Section 3 of
- # ; [RFC4515], subject to the
- # ; provisions of the
- # ; "Percent-Encoding" section
- # ; below.
- #
- # scheme = "ldap" / "ldaps" <== not RFC4516 compliant (original is 'scheme = "ldap"')
- # dn = distinguishedName ; From Section 3 of [RFC4514],
- # ; subject to the provisions of
- # ; the "Percent-Encoding"
- # ; section below.
- #
- # attributes = attrdesc *(COMMA attrdesc)
- # attrdesc = selector *(COMMA selector)
- # selector = attributeSelector ; From Section 4.5.1 of
- # ; [RFC4511], subject to the
- # ; provisions of the
- # ; "Percent-Encoding" section
- # ; below.
- #
- # scope = "base" / "one" / "sub"
- # extensions = extension *(COMMA extension)
- # extension = [EXCLAMATION] extype [EQUALS exvalue]
- # extype = oid ; From section 1.4 of [RFC4512].
- #
- # exvalue = LDAPString ; From section 4.1.2 of
- # ; [RFC4511], subject to the
- # ; provisions of the
- # ; "Percent-Encoding" section
- # ; below.
- #
- # EXCLAMATION = %x21 ; exclamation mark ("!")
- # SLASH = %x2F ; forward slash ("/")
- # COLON = %x3A ; colon (":")
- # QUESTION = %x3F ; question mark ("?")
-
- uri_components = dict()
- parts = unquote(uri).split('?') # encoding defaults to utf-8 in Python 3
- scheme, sep, remain = parts[0].partition('://')
- if sep != '://' or scheme not in ['ldap', 'ldaps']:
- return None
-
- address, _, uri_components['base'] = remain.partition('/')
-
- uri_components['ssl'] = True if scheme == 'ldaps' else False
- uri_components['host'], sep, uri_components['port'] = address.partition(':')
- if sep != ':':
- if uri_components['ssl']:
- uri_components['port'] = 636
- else:
- uri_components['port'] = None
- else:
- if not uri_components['port'].isdigit() or not (0 < int(uri_components['port']) < 65536):
- return None
- else:
- uri_components['port'] = int(uri_components['port'])
-
- uri_components['attributes'] = parts[1].split(',') if len(parts) > 1 else None
- uri_components['scope'] = parts[2] if len(parts) > 2 else None
- if uri_components['scope'] == 'base':
- uri_components['scope'] = BASE
- elif uri_components['scope'] == 'sub':
- uri_components['scope'] = SUBTREE
- elif uri_components['scope'] == 'one':
- uri_components['scope'] = LEVEL
- elif uri_components['scope']:
- return None
-
- uri_components['filter'] = parts[3] if len(parts) > 3 else None
- uri_components['extensions'] = parts[4].split(',') if len(parts) > 4 else None
-
- return uri_components
+"""
+"""
+
+# Created on 2014.09.08
+#
+# Author: Giovanni Cannata
+#
+# Copyright 2014 - 2020 Giovanni Cannata
+#
+# This file is part of ldap3.
+#
+# ldap3 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ldap3 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with ldap3 in the COPYING and COPYING.LESSER files.
+# If not, see <http://www.gnu.org/licenses/>.
+
+try:
+ from urllib.parse import unquote # Python3
+except ImportError:
+ from urllib import unquote # Python 2
+
+from .. import SUBTREE, BASE, LEVEL
+
+
+def parse_uri(uri):
+ """
+ Decode LDAP URI as specified in RFC 4516 relaxing specifications
+ permitting 'ldaps' as scheme for ssl-ldap
+ """
+
+ # ldapurl = scheme COLON SLASH SLASH [host [COLON port]]
+ # [SLASH dn [QUESTION [attributes]
+ # [QUESTION [scope] [QUESTION [filter]
+ # [QUESTION extensions]]]]]
+ # ; <host> and <port> are defined
+ # ; in Sections 3.2.2 and 3.2.3
+ # ; of [RFC3986].
+ # ; <filter> is from Section 3 of
+ # ; [RFC4515], subject to the
+ # ; provisions of the
+ # ; "Percent-Encoding" section
+ # ; below.
+ #
+ # scheme = "ldap" / "ldaps" <== not RFC4516 compliant (original is 'scheme = "ldap"')
+ # dn = distinguishedName ; From Section 3 of [RFC4514],
+ # ; subject to the provisions of
+ # ; the "Percent-Encoding"
+ # ; section below.
+ #
+ # attributes = attrdesc *(COMMA attrdesc)
+ # attrdesc = selector *(COMMA selector)
+ # selector = attributeSelector ; From Section 4.5.1 of
+ # ; [RFC4511], subject to the
+ # ; provisions of the
+ # ; "Percent-Encoding" section
+ # ; below.
+ #
+ # scope = "base" / "one" / "sub"
+ # extensions = extension *(COMMA extension)
+ # extension = [EXCLAMATION] extype [EQUALS exvalue]
+ # extype = oid ; From section 1.4 of [RFC4512].
+ #
+ # exvalue = LDAPString ; From section 4.1.2 of
+ # ; [RFC4511], subject to the
+ # ; provisions of the
+ # ; "Percent-Encoding" section
+ # ; below.
+ #
+ # EXCLAMATION = %x21 ; exclamation mark ("!")
+ # SLASH = %x2F ; forward slash ("/")
+ # COLON = %x3A ; colon (":")
+ # QUESTION = %x3F ; question mark ("?")
+
+ uri_components = dict()
+ parts = unquote(uri).split('?') # encoding defaults to utf-8 in Python 3
+ scheme, sep, remain = parts[0].partition('://')
+ if sep != '://' or scheme not in ['ldap', 'ldaps']:
+ return None
+
+ address, _, uri_components['base'] = remain.partition('/')
+
+ uri_components['ssl'] = True if scheme == 'ldaps' else False
+ uri_components['host'], sep, uri_components['port'] = address.partition(':')
+ if sep != ':':
+ if uri_components['ssl']:
+ uri_components['port'] = 636
+ else:
+ uri_components['port'] = None
+ else:
+ if not uri_components['port'].isdigit() or not (0 < int(uri_components['port']) < 65536):
+ return None
+ else:
+ uri_components['port'] = int(uri_components['port'])
+
+ uri_components['attributes'] = parts[1].split(',') if len(parts) > 1 and parts[1] else None
+ uri_components['scope'] = parts[2] if len(parts) > 2 else None
+ if uri_components['scope'] == 'base':
+ uri_components['scope'] = BASE
+ elif uri_components['scope'] == 'sub':
+ uri_components['scope'] = SUBTREE
+ elif uri_components['scope'] == 'one':
+ uri_components['scope'] = LEVEL
+ elif uri_components['scope']:
+ return None
+
+ uri_components['filter'] = parts[3] if len(parts) > 3 else None
+ uri_components['extensions'] = parts[4].split(',') if len(parts) > 4 else None
+
+ return uri_components
diff --git a/ldap3/version.py b/ldap3/version.py
index 531a578..d9ef535 100644
--- a/ldap3/version.py
+++ b/ldap3/version.py
@@ -1,9 +1,9 @@
# THIS FILE IS AUTO-GENERATED. PLEASE DO NOT MODIFY# version file for ldap3
-# generated on 2020-03-01 22:10:42.830758
-# on system uname_result(system='Windows', node='ELITE10GC', release='10', version='10.0.18362', machine='AMD64', processor='Intel64 Family 6 Model 58 Stepping 9, GenuineIntel')
-# with Python 3.8.1 - ('tags/v3.8.1:1b293b6', 'Dec 18 2019 23:11:46') - MSC v.1916 64 bit (AMD64)
+# generated on 2020-09-07 08:48:35.409733
+# on system uname_result(system='Windows', node='ELITE10GC', release='10', version='10.0.19041', machine='AMD64', processor='Intel64 Family 6 Model 58 Stepping 9, GenuineIntel')
+# with Python 3.8.5 - ('tags/v3.8.5:580fbb0', 'Jul 20 2020 15:57:54') - MSC v.1924 64 bit (AMD64)
#
-__version__ = '2.7'
+__version__ = '2.8.1'
__author__ = 'Giovanni Cannata'
__email__ = 'cannatag@gmail.com'
__url__ = 'https://github.com/cannatag/ldap3'
diff --git a/requirements.txt b/requirements.txt
index 40bbc6c..3b05c2d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1 @@
-pyasn1>=0.1.8
+pyasn1>=0.4.6
diff --git a/test/testAbandonOperation.py b/test/testAbandonOperation.py
index 1a25b38..83cff08 100644
--- a/test/testAbandonOperation.py
+++ b/test/testAbandonOperation.py
@@ -25,7 +25,7 @@
import unittest
-from test.config import random_id, get_connection, drop_connection
+from test.config import random_id, get_connection, drop_connection, get_response_values
testcase_id = ''
@@ -43,15 +43,15 @@ class Test(unittest.TestCase):
def test_abandon_0(self):
# abandon(0) should work as a "ping" to the server
- result = self.connection.abandon(0)
- self.assertTrue(result)
+ status, result, response, request = get_response_values(self.connection.abandon(0), self.connection)
+ self.assertTrue(status)
def test_abandon_1(self):
# should abandon a specific operation, but messageID 1 has been used by the authentication
- result = self.connection.abandon(1)
- self.assertFalse(result)
+ status, result, response, request = get_response_values(self.connection.abandon(1), self.connection)
+ self.assertFalse(status)
def test_abandon_99999999(self):
# should abandon a not yet existing specific operation
- result = self.connection.abandon(99999999)
- self.assertFalse(result)
+ status, result, response, request = get_response_values(self.connection.abandon(99999999), self.connection)
+ self.assertFalse(status)
diff --git a/test/testAddMembersToGroups.py b/test/testAddMembersToGroups.py
index 7ea8ef1..09c93a1 100644
--- a/test/testAddMembersToGroups.py
+++ b/test/testAddMembersToGroups.py
@@ -26,7 +26,7 @@
import unittest
-from test.config import add_user, add_group, get_connection, drop_connection, random_id, test_server_type
+from test.config import add_user, add_group, get_connection, drop_connection, random_id, test_server_type, get_response_values
testcase_id = ''
@@ -50,11 +50,7 @@ class Test(unittest.TestCase):
self.delete_at_teardown[1][0],
fix=False,
transaction=False)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[1][0] in (response[0]['attributes']['securityEquals'] if 'securityEquals' in response[0]['attributes'] else []))
@@ -62,11 +58,7 @@ class Test(unittest.TestCase):
else:
self.assertFalse(True, self.delete_at_teardown[1][0] + ' not found')
- result = self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[0][0] in (response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []))
@@ -92,11 +84,7 @@ class Test(unittest.TestCase):
transaction=False
)
for i in range(0, 2):
- result = self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
for j in range(3, 5):
@@ -106,11 +94,7 @@ class Test(unittest.TestCase):
self.assertFalse(True, self.delete_at_teardown[i][0] + ' not found')
for j in range(3, 5):
- result = self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
for i in range(0, 2):
@@ -127,11 +111,7 @@ class Test(unittest.TestCase):
self.delete_at_teardown[1][0],
fix=True,
transaction=True)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[1][0] in (response[0]['attributes']['securityEquals'] if 'securityEquals' in response[0]['attributes'] else []))
@@ -139,11 +119,7 @@ class Test(unittest.TestCase):
else:
self.assertFalse(True, self.delete_at_teardown[1][0] + ' not found')
- result = self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[0][0] in (response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []))
@@ -169,11 +145,7 @@ class Test(unittest.TestCase):
transaction=True
)
for i in range(0, 2):
- result = self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
for j in range(3, 5):
@@ -183,11 +155,7 @@ class Test(unittest.TestCase):
self.assertFalse(True, self.delete_at_teardown[i][0] + ' not found')
for j in range(3, 5):
- result = self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
for i in range(0, 2):
diff --git a/test/testAddOperation.py b/test/testAddOperation.py
index 40bbb2c..d311a97 100644
--- a/test/testAddOperation.py
+++ b/test/testAddOperation.py
@@ -62,8 +62,16 @@ class Test(unittest.TestCase):
self.assertDictEqual(copy_of_attributes, attributes)
def test_add_binary(self):
- bin1 = open('512b-rsa-example-cert.der','rb')
- bin2 = open('1024b-rsa-example-cert.der','rb')
+ try:
+ bin1 = open('512b-rsa-example-cert.der', 'rb')
+ except Exception:
+ bin1 = open('test/512b-rsa-example-cert.der', 'rb')
+
+ try:
+ bin2 = open('1024b-rsa-example-cert.der', 'rb')
+ except Exception:
+ bin2 = open('test/1024b-rsa-example-cert.der', 'rb')
+
der_certificates = [bin1.read(), bin2.read()]
bin1.close()
bin2.close()
diff --git a/test/testBytesOperation.py b/test/testBytesOperation.py
index 3020d1b..e2be165 100644
--- a/test/testBytesOperation.py
+++ b/test/testBytesOperation.py
@@ -28,7 +28,7 @@ import unittest
from time import sleep
from ldap3 import MODIFY_REPLACE, MODIFY_ADD, LEVEL, BASE, SEQUENCE_TYPES
-from test.config import random_id, get_connection, add_user, drop_connection, test_singlevalued_attribute, test_multivalued_attribute, test_base, test_name_attr
+from test.config import random_id, get_connection, add_user, drop_connection, test_singlevalued_attribute, test_multivalued_attribute, test_base, test_name_attr, get_response_values
testcase_id = ''
@@ -75,11 +75,7 @@ class Test(unittest.TestCase):
multi = [b'abc', b'def']
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-1', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -89,11 +85,7 @@ class Test(unittest.TestCase):
multi = [make_bytes('àèì', 'utf-8'), make_bytes('òù', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-2', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
@@ -103,11 +95,7 @@ class Test(unittest.TestCase):
multi = [make_bytes([195, 160, 195, 168, 195, 172]), make_bytes([195, 178, 195, 185])]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-3', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -117,11 +105,7 @@ class Test(unittest.TestCase):
multi = [make_bytes(u'\u00e0\u00e8\u00ec', 'utf-8'), make_bytes('\u00f2\u00f9', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-4', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -132,11 +116,7 @@ class Test(unittest.TestCase):
multi = [make_bytes('\N{LATIN SMALL LETTER A WITH GRAVE}\N{LATIN SMALL LETTER E WITH GRAVE}\N{LATIN SMALL LETTER I WITH GRAVE}', 'utf-8'), make_bytes('\N{LATIN SMALL LETTER O WITH GRAVE}\N{LATIN SMALL LETTER U WITH GRAVE}', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-5', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -146,11 +126,7 @@ class Test(unittest.TestCase):
multi = [make_bytearray('àèì', 'utf-8'), make_bytearray('òù', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-6', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -160,11 +136,7 @@ class Test(unittest.TestCase):
multi = [make_bytearray([195, 160, 195, 168, 195, 172]), make_bytearray([195, 178, 195, 185])]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-7', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -174,11 +146,7 @@ class Test(unittest.TestCase):
multi = [make_bytearray(u'\u00e0\u00e8\u00ec', 'utf-8'), make_bytearray(u'\u00f2\u00f9', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-8', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -189,11 +157,7 @@ class Test(unittest.TestCase):
multi = [make_bytearray(u'\N{LATIN SMALL LETTER A WITH GRAVE}\N{LATIN SMALL LETTER E WITH GRAVE}\N{LATIN SMALL LETTER I WITH GRAVE}', 'utf-8'), make_bytearray('\N{LATIN SMALL LETTER O WITH GRAVE}\N{LATIN SMALL LETTER U WITH GRAVE}', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-9', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -229,22 +193,14 @@ class Test(unittest.TestCase):
multi_mod = [b'cba', b'fed']
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-12', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
self.connection.modify(self.delete_at_teardown[0][0], {test_singlevalued_attribute: (MODIFY_REPLACE, single_mod), test_multivalued_attribute: (MODIFY_ADD, multi_mod)})
if not self.connection.strategy.sync:
sleep(2)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single_mod])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi + multi_mod))
@@ -255,22 +211,14 @@ class Test(unittest.TestCase):
multi_mod = [make_bytes('ìèà', 'utf-8'), make_bytes('ùò', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-12', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
self.connection.modify(self.delete_at_teardown[0][0], {test_singlevalued_attribute: (MODIFY_REPLACE, single_mod), test_multivalued_attribute: (MODIFY_ADD, multi_mod)})
if not self.connection.strategy.sync:
sleep(2)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single_mod])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi + multi_mod))
@@ -282,22 +230,14 @@ class Test(unittest.TestCase):
multi_mod = [make_bytes([195, 172, 195, 168, 195, 160]), make_bytes([195, 185, 195, 178])]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-13', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
self.connection.modify(self.delete_at_teardown[0][0], {test_singlevalued_attribute: (MODIFY_REPLACE, single_mod), test_multivalued_attribute: (MODIFY_ADD, multi_mod)})
if not self.connection.strategy.sync:
sleep(2)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single_mod])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi + multi_mod))
@@ -308,22 +248,14 @@ class Test(unittest.TestCase):
multi_mod = [make_bytes(u'\u00ec\u00e8\u00e0', 'utf-8'), make_bytes('\u00f9\u00f2', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-14', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
self.connection.modify(self.delete_at_teardown[0][0], {test_singlevalued_attribute: (MODIFY_REPLACE, single_mod), test_multivalued_attribute: (MODIFY_ADD, multi_mod)})
if not self.connection.strategy.sync:
sleep(2)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single_mod])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi + multi_mod))
@@ -335,22 +267,14 @@ class Test(unittest.TestCase):
multi_mod = [make_bytes('\N{LATIN SMALL LETTER I WITH GRAVE}\N{LATIN SMALL LETTER E WITH GRAVE}\N{LATIN SMALL LETTER A WITH GRAVE}', 'utf-8'), make_bytes('\N{LATIN SMALL LETTER U WITH GRAVE}\N{LATIN SMALL LETTER O WITH GRAVE}', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-15', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
self.connection.modify(self.delete_at_teardown[0][0], {test_singlevalued_attribute: (MODIFY_REPLACE, single_mod), test_multivalued_attribute: (MODIFY_ADD, multi_mod)})
if not self.connection.strategy.sync:
sleep(2)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single_mod])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi + multi_mod))
@@ -361,22 +285,14 @@ class Test(unittest.TestCase):
multi_mod = [make_bytearray('ìèà', 'utf-8'), make_bytearray('ùò', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-16', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
self.connection.modify(self.delete_at_teardown[0][0], {test_singlevalued_attribute: (MODIFY_REPLACE, single_mod), test_multivalued_attribute: (MODIFY_ADD, multi_mod)})
if not self.connection.strategy.sync:
sleep(2)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single_mod])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi + multi_mod))
@@ -387,22 +303,14 @@ class Test(unittest.TestCase):
multi_mod = [make_bytearray([195, 172, 195, 168, 195, 160]), make_bytearray([195, 185, 195, 178])]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-17', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
self.connection.modify(self.delete_at_teardown[0][0], {test_singlevalued_attribute: (MODIFY_REPLACE, single_mod), test_multivalued_attribute: (MODIFY_ADD, multi_mod)})
if not self.connection.strategy.sync:
sleep(2)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single_mod])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi + multi_mod))
@@ -413,22 +321,14 @@ class Test(unittest.TestCase):
multi_mod = [make_bytearray(u'\u00ec\u00e8\u00e0', 'utf-8'), make_bytearray('\u00f9\u00f2', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-18', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
self.connection.modify(self.delete_at_teardown[0][0], {test_singlevalued_attribute: (MODIFY_REPLACE, single_mod), test_multivalued_attribute: (MODIFY_ADD, multi_mod)})
if not self.connection.strategy.sync:
sleep(2)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single_mod])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi + multi_mod))
@@ -440,22 +340,14 @@ class Test(unittest.TestCase):
multi_mod = [make_bytearray('\N{LATIN SMALL LETTER I WITH GRAVE}\N{LATIN SMALL LETTER E WITH GRAVE}\N{LATIN SMALL LETTER A WITH GRAVE}', 'utf-8'), make_bytearray('\N{LATIN SMALL LETTER U WITH GRAVE}\N{LATIN SMALL LETTER O WITH GRAVE}', 'utf-8')]
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'byt-19', attributes={test_singlevalued_attribute: single, test_multivalued_attribute: multi}, test_bytes=True))
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
self.connection.modify(self.delete_at_teardown[0][0], {test_singlevalued_attribute: (MODIFY_REPLACE, single_mod), test_multivalued_attribute: (MODIFY_ADD, multi_mod)})
if not self.connection.strategy.sync:
sleep(2)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', BASE, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single_mod])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi + multi_mod))
@@ -470,11 +362,7 @@ class Test(unittest.TestCase):
# byte_filter = b'(&(%b=*%b*)(%b=%b))' % (make_bytes(test_name_attr, 'utf-8'), make_bytes(testcase_id, 'utf-8'), make_bytes(test_singlevalued_attribute, 'utf-8'), single)
byte_filter = b'(&(' + make_bytes(test_name_attr, 'utf-8') + b'=*' + make_bytes(testcase_id, 'utf-8') + b'*)(' + make_bytes(test_singlevalued_attribute, 'utf-8') + b'=' + single + b'))'
- result = self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -489,11 +377,7 @@ class Test(unittest.TestCase):
else:
# byte_filter = b'(&(%b=*%b*)(%b=%b))' % (make_bytes(test_name_attr, 'utf-8'), make_bytes(testcase_id, 'utf-8'), make_bytes(test_singlevalued_attribute, 'utf-8'), single)
byte_filter = b'(&(' + make_bytes(test_name_attr, 'utf-8') + b'=*' + make_bytes(testcase_id, 'utf-8') + b'*)(' + make_bytes(test_singlevalued_attribute, 'utf-8') + b'=' + single + b'))'
- result = self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -506,11 +390,7 @@ class Test(unittest.TestCase):
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
# byte_filter = b'(&(%b=*%b*)(%b=%b))' % (make_bytes(test_name_attr, 'utf-8'), make_bytes(testcase_id, 'utf-8'), make_bytes(test_singlevalued_attribute, 'utf-8'), single)
byte_filter = b'(&(' + make_bytes(test_name_attr, 'utf-8') + b'=*' + make_bytes(testcase_id, 'utf-8') + b'*)(' + make_bytes(test_singlevalued_attribute, 'utf-8') + b'=' + single + b'))'
- result = self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -525,11 +405,7 @@ class Test(unittest.TestCase):
else:
# byte_filter = b'(&(%b=*%b*)(%b=%b))' % (make_bytes(test_name_attr, 'utf-8'), make_bytes(testcase_id, 'utf-8'), make_bytes(test_singlevalued_attribute, 'utf-8'), single)
byte_filter = b'(&(' + make_bytes(test_name_attr, 'utf-8') + b'=*' + make_bytes(testcase_id, 'utf-8') + b'*)(' + make_bytes(test_singlevalued_attribute, 'utf-8') + b'=' + single + b'))'
- result = self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -545,11 +421,7 @@ class Test(unittest.TestCase):
else:
# byte_filter = b'(&(%b=*%b*)(%b=%b))' % (make_bytes(test_name_attr, 'utf-8'), make_bytes(testcase_id, 'utf-8'), make_bytes(test_singlevalued_attribute, 'utf-8'), single)
byte_filter = b'(&(' + make_bytes(test_name_attr, 'utf-8') + b'=*' + make_bytes(testcase_id, 'utf-8') + b'*)(' + make_bytes(test_singlevalued_attribute, 'utf-8') + b'=' + single + b'))'
- result = self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -564,11 +436,7 @@ class Test(unittest.TestCase):
else:
# byte_filter = b'(&(%b=*%b*)(%b=%b))' % (make_bytes(test_name_attr, 'utf-8'), make_bytes(testcase_id, 'utf-8'), make_bytes(test_singlevalued_attribute, 'utf-8'), single)
byte_filter = b'(&(' + make_bytes(test_name_attr, 'utf-8') + b'=*' + make_bytes(testcase_id, 'utf-8') + b'*)(' + make_bytes(test_singlevalued_attribute, 'utf-8') + b'=' + single + b'))'
- result = self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -583,11 +451,7 @@ class Test(unittest.TestCase):
else:
# byte_filter = b'(&(%b=*%b*)(%b=%b))' % (make_bytes(test_name_attr, 'utf-8'), make_bytes(testcase_id, 'utf-8'), make_bytes(test_singlevalued_attribute, 'utf-8'), single)
byte_filter = b'(&(' + make_bytes(test_name_attr, 'utf-8') + b'=*' + make_bytes(testcase_id, 'utf-8') + b'*)(' + make_bytes(test_singlevalued_attribute, 'utf-8') + b'=' + single + b'))'
- result = self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -602,11 +466,7 @@ class Test(unittest.TestCase):
else:
# byte_filter = b'(&(%b=*%b*)(%b=%b))' % (make_bytes(test_name_attr, 'utf-8'), make_bytes(testcase_id, 'utf-8'), make_bytes(test_singlevalued_attribute, 'utf-8'), single)
byte_filter = b'(&(' + make_bytes(test_name_attr, 'utf-8') + b'=*' + make_bytes(testcase_id, 'utf-8') + b'*)(' + make_bytes(test_singlevalued_attribute, 'utf-8') + b'=' + single + b'))'
- result = self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
@@ -619,11 +479,7 @@ class Test(unittest.TestCase):
self.assertEqual('success', self.delete_at_teardown[0][1]['description'])
# byte_filter = b'(&(%b=*%b*)(%b=%b))' % (make_bytes(test_name_attr, 'utf-8'), make_bytes(testcase_id, 'utf-8'), make_bytes(test_singlevalued_attribute, 'utf-8'), single)
byte_filter = b'(&(' + make_bytes(test_name_attr, 'utf-8') + b'=*' + make_bytes(testcase_id, 'utf-8') + b'*)(' + make_bytes(test_singlevalued_attribute, 'utf-8') + b'=' + single + b'))'
- result = self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
+ status, result, response, request = get_response_values(self.connection.search(test_base, byte_filter, attributes=[test_singlevalued_attribute, test_multivalued_attribute]), self.connection)
self.assertEqual(len(response), 1)
self.assertEqual(response[0]['raw_attributes'][test_singlevalued_attribute], [single])
self.assertEqual(sorted(response[0]['raw_attributes'][test_multivalued_attribute]), sorted(multi))
diff --git a/test/testCheckNamesFalse.py b/test/testCheckNamesFalse.py
index 4715593..c879c95 100644
--- a/test/testCheckNamesFalse.py
+++ b/test/testCheckNamesFalse.py
@@ -27,7 +27,7 @@ import unittest
from ldap3 import ALL
from ldap3.core.exceptions import LDAPAttributeError, LDAPObjectClassError
-from test.config import test_base, generate_dn, test_name_attr, random_id, get_connection, add_user, drop_connection
+from test.config import test_base, generate_dn, test_name_attr, random_id, get_connection, add_user, drop_connection, get_response_values
testcase_id = ''
@@ -58,23 +58,13 @@ class Test(unittest.TestCase):
def test_valid_assertion(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'chk-1'))
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-1)', attributes=[test_name_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-1)', attributes=[test_name_attr]), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
def test_valid_attribute(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'chk-2', attributes={'givenName': 'given-name-2'}))
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-2)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-2)', attributes=[test_name_attr, 'givenName']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
diff --git a/test/testCheckNamesTrue.py b/test/testCheckNamesTrue.py
index 6976d68..9c073ce 100644
--- a/test/testCheckNamesTrue.py
+++ b/test/testCheckNamesTrue.py
@@ -27,7 +27,7 @@ import unittest
from ldap3 import ALL
from ldap3.core.exceptions import LDAPAttributeError, LDAPObjectClassError
-from test.config import test_base, generate_dn, test_name_attr, random_id, get_connection, add_user, drop_connection
+from test.config import test_base, generate_dn, test_name_attr, random_id, get_connection, add_user, drop_connection, get_response_values
testcase_id = ''
@@ -58,23 +58,13 @@ class Test(unittest.TestCase):
def test_valid_assertion(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'chk-1'))
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-1)', attributes=[test_name_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-1)', attributes=[test_name_attr]), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
def test_valid_attribute(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'chk-2', attributes={'givenName': 'given-name-2'}))
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-2)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-2)', attributes=[test_name_attr, 'givenName']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
diff --git a/test/testCheckedAttributes.py b/test/testCheckedAttributes.py
index 89bbe4c..f57d0c7 100644
--- a/test/testCheckedAttributes.py
+++ b/test/testCheckedAttributes.py
@@ -26,7 +26,7 @@
import unittest
from ldap3 import ALL
-from test.config import test_base, test_name_attr, random_id, get_connection, add_user, drop_connection, test_int_attr, test_server_type
+from test.config import test_base, test_name_attr, random_id, get_connection, add_user, drop_connection, test_int_attr, test_server_type, get_response_values
testcase_id = ''
@@ -49,12 +49,7 @@ class Test(unittest.TestCase):
self.assertFalse(self.connection.bound)
def test_search_checked_attributes(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-1*)', attributes=[test_name_attr, 'sn', test_int_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-1*)', attributes=[test_name_attr, 'sn', test_int_attr]), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
@@ -83,12 +78,7 @@ class Test(unittest.TestCase):
'2.5.4.4': lambda v: unicode(v, encoding='UTF-8')[::-1], # sn reversed
'1.3.6.1.4.1.1466.115.121.1.27': lambda v: int(v) + 1000} # integer syntax incremented by 1000
self.connection.server.custom_formatter = formatter
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-1*)', attributes=[test_name_attr, 'sn', test_int_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'chk-1*)', attributes=[test_name_attr, 'sn', test_int_attr]), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
diff --git a/test/testRemoveMembersFromGroups.py b/test/testRemoveMembersFromGroups.py
index b28358e..f5e027f 100644
--- a/test/testRemoveMembersFromGroups.py
+++ b/test/testRemoveMembersFromGroups.py
@@ -26,7 +26,7 @@
import unittest
-from test.config import test_base, add_user, add_group, get_connection, drop_connection, random_id, test_server_type
+from test.config import test_base, add_user, add_group, get_connection, drop_connection, random_id, test_server_type, get_response_values
testcase_id = ''
@@ -53,11 +53,7 @@ class Test(unittest.TestCase):
fix=True,
transaction=False)
# verifies user in group-1
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[1][0] in (response[0]['attributes']['securityEquals'] if 'securityEquals' in response[0]['attributes'] else []))
@@ -65,11 +61,7 @@ class Test(unittest.TestCase):
else:
self.assertFalse(True, self.delete_at_teardown[1][0] + ' not found')
- result = self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[0][0] in (response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []))
@@ -78,11 +70,7 @@ class Test(unittest.TestCase):
self.assertFalse(True, self.delete_at_teardown[0][0] + ' not found')
# verifies user in group-1b
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[2][0] in (response[0]['attributes']['securityEquals'] if 'securityEquals' in response[0]['attributes'] else []))
@@ -90,11 +78,7 @@ class Test(unittest.TestCase):
else:
self.assertFalse(True, self.delete_at_teardown[2][0] + ' not found')
- result = self.connection.search(self.delete_at_teardown[2][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[2][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[0][0] in (response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []))
@@ -108,11 +92,7 @@ class Test(unittest.TestCase):
transaction=False)
# verifies users not in group-1
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[1][0] not in (response[0]['attributes']['securityEquals'] if 'securityEquals' in response[0]['attributes'] else []))
@@ -120,11 +100,7 @@ class Test(unittest.TestCase):
else:
self.assertFalse(True, self.delete_at_teardown[1][0] + ' not found')
- result = self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[0][0] not in (response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []))
@@ -133,11 +109,7 @@ class Test(unittest.TestCase):
self.assertFalse(True, self.delete_at_teardown[0][0] + ' not found')
# verifies user still in group-1b
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[2][0] in (response[0]['attributes']['securityEquals'] if 'securityEquals' in response[0]['attributes'] else []))
@@ -145,11 +117,7 @@ class Test(unittest.TestCase):
else:
self.assertFalse(True, self.delete_at_teardown[2][0] + ' not found')
- result = self.connection.search(self.delete_at_teardown[2][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[2][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[0][0] in (response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []))
@@ -175,11 +143,7 @@ class Test(unittest.TestCase):
transaction=False
)
for i in range(0, 2):
- result = self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
for j in range(3, 5):
@@ -189,11 +153,7 @@ class Test(unittest.TestCase):
self.assertFalse(True, self.delete_at_teardown[i][0] + ' not found')
for j in range(3, 5):
- result = self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
for i in range(0, 2):
@@ -213,11 +173,7 @@ class Test(unittest.TestCase):
)
for i in range(0, 2):
- result = self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
for j in range(3, 5):
@@ -227,11 +183,7 @@ class Test(unittest.TestCase):
self.assertFalse(True, self.delete_at_teardown[i][0] + ' not found')
for j in range(3, 5):
- result = self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
for i in range(0, 2):
@@ -248,11 +200,7 @@ class Test(unittest.TestCase):
self.delete_at_teardown[1][0],
fix=True,
transaction=True)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[1][0] in (response[0]['attributes']['securityEquals'] if 'securityEquals' in response[0]['attributes'] else []))
@@ -260,11 +208,7 @@ class Test(unittest.TestCase):
else:
self.assertFalse(True, self.delete_at_teardown[1][0] + ' not found')
- result = self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[0][0] in (response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []))
@@ -277,11 +221,7 @@ class Test(unittest.TestCase):
fix=False,
transaction=False)
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[1][0] not in (response[0]['attributes']['securityEquals'] if 'securityEquals' in response[0]['attributes'] else []))
@@ -289,11 +229,7 @@ class Test(unittest.TestCase):
else:
self.assertFalse(True, self.delete_at_teardown[1][0] + ' not found')
- result = self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[1][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
self.assertTrue(self.delete_at_teardown[0][0] not in (response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []))
@@ -319,11 +255,7 @@ class Test(unittest.TestCase):
transaction=True
)
for i in range(0, 2):
- result = self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
for j in range(3, 5):
@@ -333,11 +265,7 @@ class Test(unittest.TestCase):
self.assertFalse(True, self.delete_at_teardown[i][0] + ' not found')
for j in range(3, 5):
- result = self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
for i in range(0, 2):
@@ -357,11 +285,7 @@ class Test(unittest.TestCase):
)
for i in range(0, 2):
- result = self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[i][0], '(objectclass=*)', attributes=['securityEquals', 'groupMembership']), self.connection)
if response:
for j in range(3, 5):
@@ -371,11 +295,7 @@ class Test(unittest.TestCase):
self.assertFalse(True, self.delete_at_teardown[i][0] + ' not found')
for j in range(3, 5):
- result = self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[j][0], '(objectclass=*)', attributes=['member', 'equivalentToMe']), self.connection)
if response:
for i in range(0, 2):
diff --git a/test/testRestartable.py b/test/testRestartable.py
index 3dc46fb..735bd4c 100644
--- a/test/testRestartable.py
+++ b/test/testRestartable.py
@@ -31,7 +31,7 @@ from ldap3 import Server, Connection, ServerPool, RESTARTABLE, ROUND_ROBIN, BASE
class Test(unittest.TestCase):
def test_restartable_invalid_server(self):
- if test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
+ if test_server_type != 'NONE' and test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
if isinstance(test_server, (list, tuple)):
hosts = ['a.b.c.d'] + list(test_server)
else:
@@ -49,7 +49,7 @@ class Test(unittest.TestCase):
self.assertEqual(len(search_results), 1)
def test_restartable_invalid_server2(self):
- if test_server_type != 'AD':
+ if test_server_type not in ['NONE', 'AD']:
if test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
if isinstance(test_server, (list, tuple)):
hosts = ['a.b.c.d'] + list(test_server)
diff --git a/test/testSearchAndModifyEntries.py b/test/testSearchAndModifyEntries.py
index 1fe73cb..45832e3 100644
--- a/test/testSearchAndModifyEntries.py
+++ b/test/testSearchAndModifyEntries.py
@@ -31,7 +31,7 @@ from ldap3.core.exceptions import LDAPCursorError, LDAPOperationResult, LDAPCons
from ldap3.abstract import STATUS_WRITABLE, STATUS_COMMITTED, STATUS_DELETED, STATUS_INIT, STATUS_MANDATORY_MISSING, STATUS_VIRTUAL, STATUS_PENDING_CHANGES, STATUS_READ, STATUS_READY_FOR_DELETION
from ldap3.core.results import RESULT_CONSTRAINT_VIOLATION, RESULT_ATTRIBUTE_OR_VALUE_EXISTS
from test.config import test_base, test_name_attr, random_id, get_connection, add_user, drop_connection, test_server_type, test_int_attr, test_strategy,\
- test_multivalued_attribute, test_singlevalued_attribute
+ test_multivalued_attribute, test_singlevalued_attribute, get_response_values
testcase_id = ''
@@ -58,13 +58,8 @@ class Test(unittest.TestCase):
self.assertFalse(self.connection.bound)
def get_entry(self, entry_name):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + entry_name + ')', attributes=[test_name_attr, 'givenName', test_multivalued_attribute, test_singlevalued_attribute])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + entry_name + ')', attributes=[test_name_attr, 'givenName', test_multivalued_attribute, test_singlevalued_attribute]), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(entries), 1)
return entries[0]
@@ -120,13 +115,8 @@ class Test(unittest.TestCase):
self.assertTrue(result)
counter = 20
while counter > 0: # waits for at maximum 20 times - delete operation can take some time to complete
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'del1)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'del1)', attributes=[test_name_attr, 'givenName']), self.connection)
+ entries = self.connection._get_entries(response, request)
if len(entries) == 0:
break
sleep(3)
diff --git a/test/testSearchOperation.py b/test/testSearchOperation.py
index 9804a76..5221b2e 100644
--- a/test/testSearchOperation.py
+++ b/test/testSearchOperation.py
@@ -28,7 +28,7 @@ import unittest
from ldap3.utils.conv import escape_bytes, escape_filter_chars, ldap_escape_to_bytes
from test.config import test_base, test_name_attr, random_id, get_connection, \
- add_user, drop_connection, test_server_type, test_int_attr
+ add_user, drop_connection, test_server_type, test_int_attr, get_response_values
from ldap3 import SUBTREE
testcase_id = ''
@@ -61,12 +61,7 @@ class Test(unittest.TestCase):
self.assertFalse(self.connection.bound)
def test_search_exact_match(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', attributes=[test_name_attr, 'givenName']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
@@ -75,13 +70,8 @@ class Test(unittest.TestCase):
self.assertEqual(response[0]['attributes']['givenName'][0], 'givenname-1')
def test_search_exact_match_with_get_request(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result, request = self.connection.get_response(result, get_request=True)
- self.assertEqual(request['type'], 'searchRequest')
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', attributes=[test_name_attr, 'givenName']), self.connection)
+ self.assertEqual(request['type'], 'searchRequest')
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
@@ -91,42 +81,22 @@ class Test(unittest.TestCase):
def test_search_extensible_match(self):
if test_server_type == 'EDIR' and not self.connection.strategy.no_real_dsa:
- result = self.connection.search(search_base=test_base, search_filter='(&(ou:dn:=fixtures)(objectclass=inetOrgPerson))', attributes=[test_name_attr, 'givenName', 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(ou:dn:=fixtures)(objectclass=inetOrgPerson))', attributes=[test_name_attr, 'givenName', 'sn']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertTrue(len(response) >= 2)
def test_search_present(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertTrue(len(response) >= 2)
def test_search_substring_many(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', attributes=[test_name_attr, 'givenName']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 4)
def test_search_with_operational_attributes(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], get_operational_attributes=True)
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], get_operational_attributes=True), self.connection)
self.assertEqual(result['description'], 'success')
if self.connection.check_names:
if test_server_type == 'AD':
@@ -146,23 +116,13 @@ class Test(unittest.TestCase):
paged_size = 4
total_entries = 0
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], paged_size=paged_size)
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], paged_size=paged_size), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), paged_size)
total_entries += len(response)
cookie = result['controls']['1.2.840.113556.1.4.319']['value']['cookie']
while cookie:
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], paged_size=paged_size, paged_cookie=cookie)
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], paged_size=paged_size, paged_cookie=cookie), self.connection)
self.assertEqual(result['description'], 'success')
total_entries += len(response)
self.assertTrue(len(response) <= paged_size)
@@ -171,12 +131,7 @@ class Test(unittest.TestCase):
def test_search_exact_match_with_escaped_parentheses_in_filter(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, '(s)-12', attributes={'givenName': 'givenname-12'}))
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*' + escape_bytes(')') + '*)', attributes=[test_name_attr, 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*' + escape_bytes(')') + '*)', attributes=[test_name_attr, 'sn']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
@@ -186,12 +141,7 @@ class Test(unittest.TestCase):
# def test_search_exact_match_with_parentheses_in_filter(self):
# self.delete_at_teardown.append(add_user(self.connection, testcase_id, '(search)-13', attributes={'givenName': 'givenname-13'}))
- # result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)*)', attributes=[test_name_attr, 'sn'])
- # if not self.connection.strategy.sync:
- # response, result = self.connection.get_response(result)
- # else:
- # response = self.connection.response
- # result = self.connection.result
+ # status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)*)', attributes=[test_name_attr, 'sn']), self.connection)
# self.assertEqual(result['description'], 'success')
# self.assertEqual(len(response), 1)
# if test_server_type == 'AD':
@@ -200,54 +150,27 @@ class Test(unittest.TestCase):
# self.assertEqual(response[0]['attributes'][test_name_attr][0], testcase_id + '(search)-13')
def test_search_integer_exact_match(self):
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + '=0))', attributes=[test_name_attr, test_int_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + '=0))', attributes=[test_name_attr, test_int_attr]), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 4)
def test_search_integer_less_than(self):
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' <=1))', attributes=[test_name_attr, test_int_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' <=1))', attributes=[test_name_attr, test_int_attr]), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 4)
def test_search_integer_greater_than(self):
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' >=-1))', attributes=[test_name_attr, test_int_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' >=-1))', attributes=[test_name_attr, test_int_attr]), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 4)
def test_search_not_match(self):
- result = self.connection.search(search_base=test_base,
- search_filter='(!(' + test_name_attr + '=' + testcase_id + 'sea-1))',
- attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = [entry for entry in self.connection.response if entry['dn'].lower().startswith(test_name_attr.lower() + '=' + testcase_id.lower())]
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(!(' + test_name_attr + '=' + testcase_id + 'sea-1))', attributes=[test_name_attr, 'givenName']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertTrue(len(response) >= 1)
def test_search_exact_match_with_unicode_in_filter(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + u'sea-3-\u2122)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + u'sea-3-\u2122)', attributes=[test_name_attr, 'givenName']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
@@ -256,12 +179,7 @@ class Test(unittest.TestCase):
self.assertEqual(response[0]['attributes']['givenName'][0], 'givenname-3')
def test_search_exact_match_with_unescaped_chars(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + u'sea-4-)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + u'sea-4-)', attributes=[test_name_attr, 'givenName']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
@@ -271,12 +189,7 @@ class Test(unittest.TestCase):
def test_search_exact_match_with_unescaped_backslash_in_filter(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'sea-13', attributes={'givenName': testcase_id + 'givenname\\-13'}))
- result = self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + '*\\*)', attributes=[test_name_attr, 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + '*\\*)', attributes=[test_name_attr, 'sn']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
@@ -286,12 +199,7 @@ class Test(unittest.TestCase):
def test_search_exact_match_with_escaped_backslash_in_filter(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'sea-14', attributes={'givenName': testcase_id + 'givenname\\-14'}))
- result = self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + '*\\5c*)', attributes=[test_name_attr, 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + '*\\5c*)', attributes=[test_name_attr, 'sn']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
@@ -301,12 +209,7 @@ class Test(unittest.TestCase):
def test_search_exact_match_with_escape_chars_backslash_in_filter(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'sea-15', attributes={'givenName': testcase_id + 'givenname\\-15'}))
- result = self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + '*' + escape_filter_chars('\\') + '*)', attributes=[test_name_attr, 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + '*' + escape_filter_chars('\\') + '*)', attributes=[test_name_attr, 'sn']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'AD':
@@ -317,27 +220,17 @@ class Test(unittest.TestCase):
def test_search_string_guid(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'sea-16', attributes={'givenName': testcase_id + 'givenname-16'}))
if test_server_type == 'EDIR':
- result = self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + 'givenname-16)', attributes=[test_name_attr, 'sn', 'guid'])
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + 'givenname-16)', attributes=[test_name_attr, 'sn', 'guid']), self.connection)
elif test_server_type == 'AD': # not tested on AD yet
- result = self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + 'givenname-16)', attributes=[test_name_attr, 'sn', 'objectGuid'])
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + 'givenname-16)', attributes=[test_name_attr, 'sn', 'objectGuid']), self.connection)
else: # not tested on other kind of servers
return
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'EDIR':
- result = self.connection.search(search_base=test_base, search_filter='(guid=' + response[0]['attributes']['guid'] + ')', attributes=[test_name_attr, 'sn'])
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(guid=' + response[0]['attributes']['guid'] + ')', attributes=[test_name_attr, 'sn']), self.connection)
elif test_server_type == 'AD': # not tested on AD yet
- result = self.connection.search(search_base=test_base, search_filter='(objectguid=' + response[0]['attributes']['objectguid'] + ')', attributes=[test_name_attr, 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(objectguid=' + response[0]['attributes']['objectguid'] + ')', attributes=[test_name_attr, 'sn']), self.connection)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'EDIR':
@@ -350,17 +243,11 @@ class Test(unittest.TestCase):
ldap_bytes = ldap_escape_to_bytes(ldap_escaped)
self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'sea-17', attributes={'givenName': testcase_id + 'givenname-17', 'audio': ldap_bytes}))
if test_server_type == 'EDIR':
- result = self.connection.search(search_base=test_base, search_filter='(audio=%s)' % ldap_escaped, attributes=[test_name_attr, 'sn', 'givenname', 'guid', 'audio'])
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(audio=%s)' % ldap_escaped, attributes=[test_name_attr, 'sn', 'givenname', 'guid', 'audio']), self.connection)
else: # not tested on other kind of servers
return
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response = self.connection.response
- result = self.connection.result
self.assertEqual(result['description'], 'success')
self.assertEqual(len(response), 1)
if test_server_type == 'EDIR':
self.assertEqual(response[0]['attributes'][test_name_attr][0], testcase_id + 'sea-17')
self.assertEqual(response[0]['attributes']['audio'][0], ldap_bytes)
-
diff --git a/test/testSearchOperationEntries.py b/test/testSearchOperationEntries.py
index f77a6fd..9497709 100644
--- a/test/testSearchOperationEntries.py
+++ b/test/testSearchOperationEntries.py
@@ -29,7 +29,7 @@ from ldap3 import SUBTREE
from ldap3.utils.conv import escape_bytes
from test.config import test_base, test_name_attr, random_id, get_connection, \
- add_user, drop_connection, test_server_type, test_int_attr
+ add_user, drop_connection, test_server_type, test_int_attr, get_response_values
testcase_id = ''
@@ -56,13 +56,8 @@ class Test(unittest.TestCase):
self.assertFalse(self.connection.bound)
def test_search_exact_match(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', attributes=[test_name_attr, 'givenName']), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].givenName.value, 'givenname-1')
@@ -71,46 +66,26 @@ class Test(unittest.TestCase):
def test_search_extensible_match(self):
if test_server_type == 'EDIR' and not self.connection.strategy.no_real_dsa:
- result = self.connection.search(search_base=test_base, search_filter='(&(ou:dn:=fixtures)(objectclass=inetOrgPerson))', attributes=[test_name_attr, 'givenName', 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(ou:dn:=fixtures)(objectclass=inetOrgPerson))', attributes=[test_name_attr, 'givenName', 'sn']), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertTrue(len(entries) >= 2)
def test_search_present(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName']), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertTrue(len(entries) >= 2)
def test_search_substring_many(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', attributes=[test_name_attr, 'givenName']), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(entries), 2)
def test_search_with_operational_attributes(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], get_operational_attributes=True)
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], get_operational_attributes=True), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
if self.connection.check_names:
self.assertEqual(entries[0].entry_dn.lower(), self.delete_at_teardown[0][0].lower())
@@ -127,25 +102,15 @@ class Test(unittest.TestCase):
paged_size = 4
total_entries = 0
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], paged_size=paged_size)
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], paged_size=paged_size), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(entries), paged_size)
total_entries += len(entries)
cookie = result['controls']['1.2.840.113556.1.4.319']['value']['cookie']
while cookie:
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], paged_size=paged_size, paged_cookie=cookie)
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], paged_size=paged_size, paged_cookie=cookie), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
total_entries += len(entries)
self.assertTrue(len(entries) <= paged_size)
@@ -154,46 +119,26 @@ class Test(unittest.TestCase):
def test_search_exact_match_with_parentheses_in_filter(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, '(search)-10', attributes={'givenName': 'givenname-10'}))
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*' + escape_bytes(')') + '*)', attributes=[test_name_attr, 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*' + escape_bytes(')') + '*)', attributes=[test_name_attr, 'sn']), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0][test_name_attr][0], testcase_id + '(search)-10')
def test_search_integer_exact_match(self):
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + '=0))', attributes=[test_name_attr, test_int_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + '=0))', attributes=[test_name_attr, test_int_attr]), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(entries), 2)
def test_search_integer_less_than(self):
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' <=1))', attributes=[test_name_attr, test_int_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' <=1))', attributes=[test_name_attr, test_int_attr]), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(entries), 2)
def test_search_integer_greater_than(self):
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' >=-1))', attributes=[test_name_attr, test_int_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- entries = self.connection._get_entries(response)
- else:
- result = self.connection.result
- entries = self.connection.entries
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' >=-1))', attributes=[test_name_attr, test_int_attr]), self.connection)
+ entries = self.connection._get_entries(response, request)
self.assertEqual(result['description'], 'success')
self.assertEqual(len(entries), 2)
diff --git a/test/testSearchOperationJSON.py b/test/testSearchOperationJSON.py
index dc25f44..0cd05c0 100644
--- a/test/testSearchOperationJSON.py
+++ b/test/testSearchOperationJSON.py
@@ -28,7 +28,7 @@ import json
from ldap3 import SUBTREE
from ldap3.utils.conv import escape_bytes
-from test.config import test_base, test_name_attr, random_id, get_connection, add_user, drop_connection, test_int_attr, test_server_type
+from test.config import test_base, test_name_attr, random_id, get_connection, add_user, drop_connection, test_int_attr, test_server_type, get_response_values
testcase_id = ''
@@ -54,45 +54,27 @@ class Test(unittest.TestCase):
self.assertFalse(self.connection.bound)
def test_search_exact_match(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, _ = self.connection.get_response(result)
- json_response = self.connection.response_to_json(search_result=response)
- else:
- json_response = self.connection.response_to_json()
-
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', attributes=[test_name_attr, 'givenName']), self.connection)
+ json_response = self.connection.response_to_json(search_result=response)
json_entries = json.loads(json_response)['entries']
self.assertEqual(len(json_entries), 1)
def test_search_extensible_match(self):
if test_server_type == 'EDIR' and not self.connection.strategy.no_real_dsa:
- result = self.connection.search(search_base=test_base, search_filter='(&(ou:dn:=fixtures)(objectclass=inetOrgPerson))', attributes=[test_name_attr, 'givenName', 'sn'])
- if not self.connection.strategy.sync:
- response, _ = self.connection.get_response(result)
- json_response = self.connection.response_to_json(search_result=response)
- else:
- json_response = self.connection.response_to_json()
-
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(ou:dn:=fixtures)(objectclass=inetOrgPerson))', attributes=[test_name_attr, 'givenName', 'sn']), self.connection)
+ json_response = self.connection.response_to_json(search_result=response)
json_entries = json.loads(json_response)['entries']
self.assertTrue(len(json_entries) >= 2)
def test_search_present(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, _ = self.connection.get_response(result)
- json_response = self.connection.response_to_json(search_result=response)
- else:
- json_response = self.connection.response_to_json()
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=*)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName']), self.connection)
+ json_response = self.connection.response_to_json(search_result=response)
json_entries = json.loads(json_response)['entries']
self.assertTrue(len(json_entries) >= 2)
def test_search_substring_many(self):
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', attributes=[test_name_attr, 'givenName'])
- if not self.connection.strategy.sync:
- response, _ = self.connection.get_response(result)
- json_response = self.connection.response_to_json(search_result=response)
- else:
- json_response = self.connection.response_to_json()
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + '*)', attributes=[test_name_attr, 'givenName']), self.connection)
+ json_response = self.connection.response_to_json(search_result=response)
json_entries = json.loads(json_response)['entries']
self.assertEqual(len(json_entries), 2)
@@ -103,12 +85,8 @@ class Test(unittest.TestCase):
test_operation_attribute = 'entryDN'
else:
test_operation_attribute = 'xxx'
- result = self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], get_operational_attributes=True)
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- json_response = self.connection.response_to_json(search_result=response)
- else:
- json_response = self.connection.response_to_json()
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(' + test_name_attr + '=' + testcase_id + 'sea-1)', search_scope=SUBTREE, attributes=[test_name_attr, 'givenName'], get_operational_attributes=True), self.connection)
+ json_response = self.connection.response_to_json(search_result=response)
json_entries = json.loads(json_response)['entries']
if self.connection.check_names:
@@ -121,12 +99,8 @@ class Test(unittest.TestCase):
def test_search_exact_match_with_parentheses_in_filter(self):
self.delete_at_teardown.append(add_user(self.connection, testcase_id, '(search)-3', attributes={'givenName': 'givenname-3'}))
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_name_attr + '=*' + escape_bytes(')') + '*))', attributes=[test_name_attr, 'sn'])
- if not self.connection.strategy.sync:
- response, _ = self.connection.get_response(result)
- json_response = self.connection.response_to_json(search_result=response)
- else:
- json_response = self.connection.response_to_json()
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_name_attr + '=*' + escape_bytes(')') + '*))', attributes=[test_name_attr, 'sn']), self.connection)
+ json_response = self.connection.response_to_json(search_result=response)
json_entries = json.loads(json_response)['entries']
self.assertEqual(len(json_entries), 1)
@@ -136,34 +110,19 @@ class Test(unittest.TestCase):
self.assertEqual(json_entries[0]['attributes'][test_name_attr][0], testcase_id + '(search)-3')
def test_search_integer_exact_match(self):
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + '=0))', attributes=[test_name_attr, test_int_attr])
- if not self.connection.strategy.sync:
- response, _ = self.connection.get_response(result)
- json_response = self.connection.response_to_json(search_result=response)
- else:
- json_response = self.connection.response_to_json()
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + '=0))', attributes=[test_name_attr, test_int_attr]), self.connection)
+ json_response = self.connection.response_to_json(search_result=response)
json_entries = json.loads(json_response)['entries']
-
self.assertEqual(len(json_entries), 2)
def test_search_integer_less_than(self):
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' <=1))', attributes=[test_name_attr, test_int_attr])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- json_response = self.connection.response_to_json(search_result=response)
- else:
- json_response = self.connection.response_to_json()
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + ' <=1))', attributes=[test_name_attr, test_int_attr]), self.connection)
+ json_response = self.connection.response_to_json(search_result=response)
json_entries = json.loads(json_response)['entries']
-
self.assertEqual(len(json_entries), 2)
def test_search_integer_greater_than(self):
- result = self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + '>=-1))', attributes=[test_name_attr, test_int_attr])
- if not self.connection.strategy.sync:
- response, _ = self.connection.get_response(result)
- json_response = self.connection.response_to_json(search_result=response)
- else:
- json_response = self.connection.response_to_json()
+ status, result, response, request = get_response_values(self.connection.search(search_base=test_base, search_filter='(&(' + test_name_attr + '=' + testcase_id + '*)(' + test_int_attr + '>=-1))', attributes=[test_name_attr, test_int_attr]), self.connection)
+ json_response = self.connection.response_to_json(search_result=response)
json_entries = json.loads(json_response)['entries']
-
self.assertEqual(len(json_entries), 2)
diff --git a/test/testTls.py b/test/testTls.py
index 891a3e6..0e85940 100644
--- a/test/testTls.py
+++ b/test/testTls.py
@@ -28,14 +28,14 @@ import ssl
from ldap3 import Server, Connection, ServerPool, Tls, SASL, EXTERNAL, MOCK_ASYNC, MOCK_SYNC
from test.config import test_server, test_port, test_port_ssl, test_user, test_password, test_authentication, \
- test_strategy, test_lazy_connection, test_get_info, test_server_mode, test_valid_names, \
+ test_strategy, test_lazy_connection, test_get_info, test_server_mode, test_valid_names, test_server_type,\
test_pooling_strategy, test_pooling_active, test_pooling_exhaust, test_ca_cert_file, \
test_user_cert_file, test_user_key_file
class Test(unittest.TestCase):
def test_start_tls(self):
- if test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
+ if test_server_type != 'NONE' and test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
if isinstance(test_server, (list, tuple)):
server = ServerPool(pool_strategy=test_pooling_strategy, active=test_pooling_active, exhaust=test_pooling_exhaust)
for host in test_server:
@@ -51,7 +51,7 @@ class Test(unittest.TestCase):
connection.strategy.terminate()
def test_open_ssl_with_defaults(self):
- if test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
+ if test_server_type != 'NONE' and test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
if isinstance(test_server, (list, tuple)):
server = ServerPool(pool_strategy=test_pooling_strategy, active=test_pooling_active, exhaust=test_pooling_exhaust)
for host in test_server:
@@ -66,7 +66,7 @@ class Test(unittest.TestCase):
connection.strategy.terminate()
def test_open_with_tls_before_bind(self):
- if test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
+ if test_server_type != 'NONE' and test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
if isinstance(test_server, (list, tuple)):
server = ServerPool(pool_strategy=test_pooling_strategy, active=test_pooling_active, exhaust=test_pooling_exhaust)
for host in test_server:
@@ -84,7 +84,7 @@ class Test(unittest.TestCase):
self.assertFalse(connection.bound)
def test_open_with_tls_after_bind(self):
- if test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
+ if test_server_type != 'NONE' and test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
if isinstance(test_server, (list, tuple)):
server = ServerPool(pool_strategy=test_pooling_strategy, active=test_pooling_active, exhaust=test_pooling_exhaust)
for host in test_server:
@@ -144,7 +144,7 @@ class Test(unittest.TestCase):
# self.assertFalse(connection.bound)
def test_bind_ssl_cert_none(self):
- if test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
+ if test_server_type != 'NONE' and test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
tls = Tls(validate=ssl.CERT_NONE)
if isinstance(test_server, (list, tuple)):
server = ServerPool(pool_strategy=test_pooling_strategy, active=test_pooling_active, exhaust=test_pooling_exhaust)
@@ -166,7 +166,7 @@ class Test(unittest.TestCase):
# ciphers = '!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2'
ciphers = 'HIGH:!aNULL:!RC4:!DSS'
- if test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
+ if test_server_type != 'NONE' and test_strategy not in [MOCK_SYNC, MOCK_ASYNC]:
if isinstance(test_server, (list, tuple)):
server = ServerPool(pool_strategy=test_pooling_strategy, active=test_pooling_active, exhaust=test_pooling_exhaust)
for host in test_server:
diff --git a/test/testTransactions.py b/test/testTransactions.py
index 0f7917d..9707e79 100644
--- a/test/testTransactions.py
+++ b/test/testTransactions.py
@@ -29,7 +29,7 @@ import unittest
from ldap3 import MODIFY_REPLACE
from ldap3.protocol.controls import build_control
from ldap3.protocol.novell import Integer
-from test.config import add_user, get_connection, drop_connection, random_id, test_server_type
+from test.config import add_user, get_connection, drop_connection, random_id, test_server_type, get_response_values
testcase_id = ''
@@ -51,12 +51,8 @@ class Test(unittest.TestCase):
transaction_control = self.connection.extend.novell.start_transaction()
self.connection.modify(self.delete_at_teardown[0][0], {'givenName': (MODIFY_REPLACE, ['user-1b'])}, controls=[transaction_control])
self.connection.modify(self.delete_at_teardown[0][0], {'sn': (MODIFY_REPLACE, ['sn-user-1b'])}, controls=[transaction_control])
- result = self.connection.extend.novell.end_transaction(commit=True, controls=[transaction_control])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['givenName', 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
+ self.connection.extend.novell.end_transaction(commit=True, controls=[transaction_control])
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['givenName', 'sn']), self.connection)
if response:
self.assertEqual(response[0]['attributes']['givenName'][0], 'user-1b')
@@ -70,13 +66,8 @@ class Test(unittest.TestCase):
transaction_control = self.connection.extend.novell.start_transaction()
self.connection.modify(self.delete_at_teardown[0][0], {'givenName': (MODIFY_REPLACE, ['user-1b'])}, controls=[transaction_control])
self.connection.modify(self.delete_at_teardown[0][0], {'sn': (MODIFY_REPLACE, ['sn-user-1b'])}, controls=[transaction_control])
- result = self.connection.extend.novell.end_transaction(commit=False, controls=[transaction_control])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['givenName', 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
-
+ self.connection.extend.novell.end_transaction(commit=False, controls=[transaction_control])
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['givenName', 'sn']), self.connection)
if response:
# self.assertEqual(response[0]['attributes']['givenName'][0], 'user-1b')
self.assertEqual(response[0]['attributes']['sn'][0], 'user-1')
@@ -90,13 +81,8 @@ class Test(unittest.TestCase):
invalid_transaction_control = build_control('2.16.840.1.113719.1.27.103.7', True, Integer(12345678), encode_control_value=True)
self.connection.modify(self.delete_at_teardown[0][0], {'givenName': (MODIFY_REPLACE, ['user-1b'])}, controls=[transaction_control])
self.connection.modify(self.delete_at_teardown[0][0], {'sn': (MODIFY_REPLACE, ['sn-user-1b'])}, controls=[invalid_transaction_control])
- result = self.connection.extend.novell.end_transaction(commit=True, controls=[transaction_control])
- result = self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['givenName', 'sn'])
- if not self.connection.strategy.sync:
- response, result = self.connection.get_response(result)
- else:
- response, result = self.connection.response, self.connection.result
-
+ self.connection.extend.novell.end_transaction(commit=True, controls=[transaction_control])
+ status, result, response, request = get_response_values(self.connection.search(self.delete_at_teardown[0][0], '(objectclass=*)', attributes=['givenName', 'sn']), self.connection)
if response:
self.assertEqual(response[0]['attributes']['givenName'][0], 'user-1b')
self.assertEqual(response[0]['attributes']['sn'][0], 'user-1')