diff options
author | Andrej Shadura <andrew.shadura@collabora.co.uk> | 2020-11-30 12:16:10 +0100 |
---|---|---|
committer | Andrej Shadura <andrew.shadura@collabora.co.uk> | 2020-11-30 12:16:10 +0100 |
commit | 96fb2df0e33b34a4dbb75577de8589a8611759c4 (patch) | |
tree | 7405641226ff632199336936c602f402b0427de4 | |
parent | f6fd16282811d1a686b5ae611351b0f8eed3c008 (diff) |
New upstream version 2.8.1
54 files changed, 1193 insertions, 1416 deletions
@@ -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
@@ -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')
|