Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
connection.py1606 linesDownload Raw Back to core
1"""2"""3 4# Created on 2014.05.315#6# Author: Giovanni Cannata7#8# Copyright 2014 - 2020 Giovanni Cannata9#10# This file is part of ldap3.11#12# ldap3 is free software: you can redistribute it and/or modify13# it under the terms of the GNU Lesser General Public License as published14# by the Free Software Foundation, either version 3 of the License, or15# (at your option) any later version.16#17# ldap3 is distributed in the hope that it will be useful,18# but WITHOUT ANY WARRANTY; without even the implied warranty of19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the20# GNU Lesser General Public License for more details.21#22# You should have received a copy of the GNU Lesser General Public License23# along with ldap3 in the COPYING and COPYING.LESSER files.24# If not, see <http://www.gnu.org/licenses/>.25from copy import deepcopy, copy26from os import linesep27from threading import RLock28from functools import reduce29import json30 31from .. import ANONYMOUS, SIMPLE, SASL, MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE, get_config_parameter, DEREF_ALWAYS, \32    SUBTREE, ASYNC, SYNC, NO_ATTRIBUTES, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, MODIFY_INCREMENT, LDIF, ASYNC_STREAM, \33    RESTARTABLE, ROUND_ROBIN, REUSABLE, AUTO_BIND_DEFAULT, AUTO_BIND_NONE, AUTO_BIND_TLS_BEFORE_BIND, SAFE_SYNC, SAFE_RESTARTABLE, \34    AUTO_BIND_TLS_AFTER_BIND, AUTO_BIND_NO_TLS, STRING_TYPES, SEQUENCE_TYPES, MOCK_SYNC, MOCK_ASYNC, NTLM, EXTERNAL,\35    DIGEST_MD5, GSSAPI, PLAIN, DSA, SCHEMA, ALL36 37from .results import RESULT_SUCCESS, RESULT_COMPARE_TRUE, RESULT_COMPARE_FALSE38from ..extend import ExtendedOperationsRoot39from .pooling import ServerPool40from .server import Server41from ..operation.abandon import abandon_operation, abandon_request_to_dict42from ..operation.add import add_operation, add_request_to_dict43from ..operation.bind import bind_operation, bind_request_to_dict44from ..operation.compare import compare_operation, compare_request_to_dict45from ..operation.delete import delete_operation, delete_request_to_dict46from ..operation.extended import extended_operation, extended_request_to_dict47from ..operation.modify import modify_operation, modify_request_to_dict48from ..operation.modifyDn import modify_dn_operation, modify_dn_request_to_dict49from ..operation.search import search_operation, search_request_to_dict50from ..protocol.rfc2849 import operation_to_ldif, add_ldif_header51from ..protocol.sasl.digestMd5 import sasl_digest_md552from ..protocol.sasl.external import sasl_external53from ..protocol.sasl.plain import sasl_plain54from ..strategy.sync import SyncStrategy55from ..strategy.safeSync import SafeSyncStrategy56from ..strategy.safeRestartable import SafeRestartableStrategy57from ..strategy.mockAsync import MockAsyncStrategy58from ..strategy.asynchronous import AsyncStrategy59from ..strategy.reusable import ReusableStrategy60from ..strategy.restartable import RestartableStrategy61from ..strategy.ldifProducer import LdifProducerStrategy62from ..strategy.mockSync import MockSyncStrategy63from ..strategy.asyncStream import AsyncStreamStrategy64from ..operation.unbind import unbind_operation65from ..protocol.rfc2696 import paged_search_control66from .usage import ConnectionUsage67from .tls import Tls68from .exceptions import LDAPUnknownStrategyError, LDAPBindError, LDAPUnknownAuthenticationMethodError, \69    LDAPSASLMechanismNotSupportedError, LDAPObjectClassError, LDAPConnectionIsReadOnlyError, LDAPChangeError, LDAPExceptionError, \70    LDAPObjectError, LDAPSocketReceiveError, LDAPAttributeError, LDAPInvalidValueError, LDAPInvalidPortError, LDAPStartTLSError71 72from ..utils.conv import escape_bytes, prepare_for_stream, check_json_dict, format_json, to_unicode73from ..utils.log import log, log_enabled, ERROR, BASIC, PROTOCOL, EXTENDED, get_library_log_hide_sensitive_data74from ..utils.dn import safe_dn75from ..utils.port_validators import check_port_and_port_list76 77 78SASL_AVAILABLE_MECHANISMS = [EXTERNAL,79                             DIGEST_MD5,80                             GSSAPI,81                             PLAIN]82 83CLIENT_STRATEGIES = [SYNC,84                     SAFE_SYNC,85                     SAFE_RESTARTABLE,86                     ASYNC,87                     LDIF,88                     RESTARTABLE,89                     REUSABLE,90                     MOCK_SYNC,91                     MOCK_ASYNC,92                     ASYNC_STREAM]93 94 95def _format_socket_endpoint(endpoint):96    if endpoint and len(endpoint) == 2:  # IPv497        return str(endpoint[0]) + ':' + str(endpoint[1])98    elif endpoint and len(endpoint) == 4:  # IPv699        return '[' + str(endpoint[0]) + ']:' + str(endpoint[1])100 101    try:102        return str(endpoint)103    except Exception:104        return '?'105 106 107def _format_socket_endpoints(sock):108    if sock:109        try:110            local = sock.getsockname()111        except Exception:112            local = (None, None, None, None)113        try:114            remote = sock.getpeername()115        except Exception:116            remote = (None, None, None, None)117 118        return '<local: ' + _format_socket_endpoint(local) + ' - remote: ' + _format_socket_endpoint(remote) + '>'119    return '<no socket>'120 121 122class Connection(object):123    """Main ldap connection class.124 125    Controls, if used, must be a list of tuples. Each tuple must have 3126    elements, the control OID, a boolean meaning if the control is127    critical, a value.128 129    If the boolean is set to True the server must honor the control or130    refuse the operation131 132    Mixing controls must be defined in controls specification (as per133    RFC 4511)134 135    :param server: the Server object to connect to136    :type server: Server, str137    :param user: the user name for simple authentication138    :type user: str139    :param password: the password for simple authentication140    :type password: str141    :param auto_bind: specify if the bind will be performed automatically when defining the Connection object142    :type auto_bind: int, can be one of AUTO_BIND_DEFAULT, AUTO_BIND_NONE, AUTO_BIND_NO_TLS, AUTO_BIND_TLS_BEFORE_BIND, AUTO_BIND_TLS_AFTER_BIND as specified in ldap3143    :param version: LDAP version, default to 3144    :type version: int145    :param authentication: type of authentication146    :type authentication: int, can be one of ANONYMOUS, SIMPLE or SASL, as specified in ldap3147    :param client_strategy: communication strategy used in the Connection148    :type client_strategy: can be one of SYNC, ASYNC, LDIF, RESTARTABLE, REUSABLE as specified in ldap3149    :param auto_referrals: specify if the connection object must automatically follow referrals150    :type auto_referrals: bool151    :param sasl_mechanism: mechanism for SASL authentication, can be one of 'EXTERNAL', 'DIGEST-MD5', 'GSSAPI', 'PLAIN'152    :type sasl_mechanism: str153    :param sasl_credentials: credentials for SASL mechanism154    :type sasl_credentials: tuple155    :param check_names: if True the library will check names of attributes and object classes against the schema. Also values found in entries will be formatted as indicated by the schema156    :type check_names: bool157    :param collect_usage: collect usage metrics in the usage attribute158    :type collect_usage: bool159    :param read_only: disable operations that modify data in the LDAP server160    :type read_only: bool161    :param lazy: open and bind the connection only when an actual operation is performed162    :type lazy: bool163    :param raise_exceptions: raise exceptions when operations are not successful, if False operations return False if not successful but not raise exceptions164    :type raise_exceptions: bool165    :param pool_name: pool name for pooled strategies166    :type pool_name: str167    :param pool_size: pool size for pooled strategies168    :type pool_size: int169    :param pool_lifetime: pool lifetime for pooled strategies170    :type pool_lifetime: int171    :param cred_store: credential store for gssapi172    :type cred_store: dict173    :param use_referral_cache: keep referral connections open and reuse them174    :type use_referral_cache: bool175    :param auto_escape: automatic escaping of filter values176    :type auto_escape: bool177    :param auto_encode: automatic encoding of attribute values178    :type auto_encode: bool179    :param source_address: the ip address or hostname to use as the source when opening the connection to the server180    :type source_address: str181    :param source_port: the source port to use when opening the connection to the server. Cannot be specified with source_port_list182    :type source_port: int183    :param source_port_list: a list of source ports to choose from when opening the connection to the server. Cannot be specified with source_port184    :type source_port_list: list185    """186    def __init__(self,187                 server,188                 user=None,189                 password=None,190                 auto_bind=AUTO_BIND_DEFAULT,191                 version=3,192                 authentication=None,193                 client_strategy=SYNC,194                 auto_referrals=True,195                 auto_range=True,196                 sasl_mechanism=None,197                 sasl_credentials=None,198                 check_names=True,199                 collect_usage=False,200                 read_only=False,201                 lazy=False,202                 raise_exceptions=False,203                 pool_name=None,204                 pool_size=None,205                 pool_lifetime=None,206                 cred_store=None,207                 fast_decoder=True,208                 receive_timeout=None,209                 return_empty_attributes=True,210                 use_referral_cache=False,211                 auto_escape=True,212                 auto_encode=True,213                 pool_keepalive=None,214                 source_address=None,215                 source_port=None,216                 source_port_list=None):217 218        conf_default_pool_name = get_config_parameter('DEFAULT_THREADED_POOL_NAME')219        self.connection_lock = RLock()  # re-entrant lock to ensure that operations in the Connection object are executed atomically in the same thread220        with self.connection_lock:221            if client_strategy not in CLIENT_STRATEGIES:222                self.last_error = 'unknown client connection strategy'223                if log_enabled(ERROR):224                    log(ERROR, '%s for <%s>', self.last_error, self)225                raise LDAPUnknownStrategyError(self.last_error)226 227            self.strategy_type = client_strategy228            self.user = user229            self.password = password230 231            if not authentication and self.user:232                self.authentication = SIMPLE233            elif not authentication:234                self.authentication = ANONYMOUS235            elif authentication in [SIMPLE, ANONYMOUS, SASL, NTLM]:236                self.authentication = authentication237            else:238                self.last_error = 'unknown authentication method'239                if log_enabled(ERROR):240                    log(ERROR, '%s for <%s>', self.last_error, self)241                raise LDAPUnknownAuthenticationMethodError(self.last_error)242 243            self.version = version244            self.auto_referrals = True if auto_referrals else False245            self.request = None246            self.response = None247            self.result = None248            self.bound = False249            self.listening = False250            self.closed = True251            self.last_error = None252            if auto_bind is False:  # compatibility with older version where auto_bind was a boolean253                self.auto_bind = AUTO_BIND_DEFAULT254            elif auto_bind is True:255                self.auto_bind = AUTO_BIND_NO_TLS256            else:257                self.auto_bind = auto_bind258            self.sasl_mechanism = sasl_mechanism259            self.sasl_credentials = sasl_credentials260            self._usage = ConnectionUsage() if collect_usage else None261            self.socket = None262            self.tls_started = False263            self.sasl_in_progress = False264            self.read_only = read_only265            self._context_state = []266            self._deferred_open = False267            self._deferred_bind = False268            self._deferred_start_tls = False269            self._bind_controls = None270            self._executing_deferred = False271            self.lazy = lazy272            self.pool_name = pool_name if pool_name else conf_default_pool_name273            self.pool_size = pool_size274            self.cred_store = cred_store275            self.pool_lifetime = pool_lifetime276            self.pool_keepalive = pool_keepalive277            self.starting_tls = False278            self.check_names = check_names279            self.raise_exceptions = raise_exceptions280            self.auto_range = True if auto_range else False281            self.extend = ExtendedOperationsRoot(self)282            self._entries = []283            self.fast_decoder = fast_decoder284            self.receive_timeout = receive_timeout285            self.empty_attributes = return_empty_attributes286            self.use_referral_cache = use_referral_cache287            self.auto_escape = auto_escape288            self.auto_encode = auto_encode289            self._digest_md5_kic = None290            self._digest_md5_kis = None291            self._digest_md5_sec_num = 0292 293            port_err = check_port_and_port_list(source_port, source_port_list)294            if port_err:295                if log_enabled(ERROR):296                    log(ERROR, port_err)297                raise LDAPInvalidPortError(port_err)298            # using an empty string to bind a socket means "use the default as if this wasn't provided" because socket299            # binding requires that you pass something for the ip if you want to pass a specific port300            self.source_address = source_address if source_address is not None else ''301            # using 0 as the source port to bind a socket means "use the default behavior of picking a random port from302            # all ports as if this wasn't provided" because socket binding requires that you pass something for the port303            # if you want to pass a specific ip304            self.source_port_list = [0]305            if source_port is not None:306                self.source_port_list = [source_port]307            elif source_port_list is not None:308                self.source_port_list = source_port_list[:]309 310            if isinstance(server, STRING_TYPES):311                server = Server(server)312            if isinstance(server, SEQUENCE_TYPES):313                server = ServerPool(server, ROUND_ROBIN, active=True, exhaust=True)314 315            if isinstance(server, ServerPool):316                self.server_pool = server317                self.server_pool.initialize(self)318                self.server = self.server_pool.get_current_server(self)319            else:320                self.server_pool = None321                self.server = server322 323            # if self.authentication == SIMPLE and self.user and self.check_names:324            #     self.user = safe_dn(self.user)325            #     if log_enabled(EXTENDED):326            #         log(EXTENDED, 'user name sanitized to <%s> for simple authentication via <%s>', self.user, self)327 328            if self.strategy_type == SYNC:329                self.strategy = SyncStrategy(self)330            elif self.strategy_type == SAFE_SYNC:331                self.strategy = SafeSyncStrategy(self)332            elif self.strategy_type == SAFE_RESTARTABLE:333                self.strategy = SafeRestartableStrategy(self)334            elif self.strategy_type == ASYNC:335                self.strategy = AsyncStrategy(self)336            elif self.strategy_type == LDIF:337                self.strategy = LdifProducerStrategy(self)338            elif self.strategy_type == RESTARTABLE:339                self.strategy = RestartableStrategy(self)340            elif self.strategy_type == REUSABLE:341                self.strategy = ReusableStrategy(self)342                self.lazy = False343            elif self.strategy_type == MOCK_SYNC:344                self.strategy = MockSyncStrategy(self)345            elif self.strategy_type == MOCK_ASYNC:346                self.strategy = MockAsyncStrategy(self)347            elif self.strategy_type == ASYNC_STREAM:348                self.strategy = AsyncStreamStrategy(self)349            else:350                self.last_error = 'unknown strategy'351                if log_enabled(ERROR):352                    log(ERROR, '%s for <%s>', self.last_error, self)353                raise LDAPUnknownStrategyError(self.last_error)354 355            # maps strategy functions to connection functions356            self.send = self.strategy.send357            self.open = self.strategy.open358            self.get_response = self.strategy.get_response359            self.post_send_single_response = self.strategy.post_send_single_response360            self.post_send_search = self.strategy.post_send_search361 362            if not self.strategy.no_real_dsa:363                self._do_auto_bind()364            # else:  # for strategies with a fake server set get_info to NONE if server hasn't a schema365            #     if self.server and not self.server.schema:366            #         self.server.get_info = NONE367            if log_enabled(BASIC):368                if get_library_log_hide_sensitive_data():369                    log(BASIC, 'instantiated Connection: <%s>', self.repr_with_sensitive_data_stripped())370                else:371                    log(BASIC, 'instantiated Connection: <%r>', self)372 373    def _prepare_return_value(self, status, response=False):374        if self.strategy.thread_safe:375            temp_response = self.response376            self.response = None377            temp_request = self.request378            self.request = None379            return status, deepcopy(self.result), deepcopy(temp_response) if response else None, copy(temp_request)380        return status381 382    def _do_auto_bind(self):383        if self.auto_bind and self.auto_bind not in [AUTO_BIND_NONE, AUTO_BIND_DEFAULT]:384            if log_enabled(BASIC):385                log(BASIC, 'performing automatic bind for <%s>', self)386            if self.closed:387                self.open(read_server_info=False)388            if self.auto_bind == AUTO_BIND_NO_TLS:389                self.bind(read_server_info=True)390            elif self.auto_bind == AUTO_BIND_TLS_BEFORE_BIND:391                if self.start_tls(read_server_info=False):392                    self.bind(read_server_info=True)393                else:394                    error = 'automatic start_tls befored bind not successful' + (' - ' + self.last_error if self.last_error else '')395                    if log_enabled(ERROR):396                        log(ERROR, '%s for <%s>', error, self)397                    self.unbind()  # unbind anyway to close connection398                    raise LDAPStartTLSError(error)399            elif self.auto_bind == AUTO_BIND_TLS_AFTER_BIND:400                self.bind(read_server_info=False)401                if not self.start_tls(read_server_info=True):402                    error = 'automatic start_tls after bind not successful' + (' - ' + self.last_error if self.last_error else '')403                    if log_enabled(ERROR):404                        log(ERROR, '%s for <%s>', error, self)405                    self.unbind()406                    raise LDAPStartTLSError(error)407            if not self.bound:408                error = 'automatic bind not successful' + (' - ' + self.last_error if self.last_error else '')409                if log_enabled(ERROR):410                    log(ERROR, '%s for <%s>', error, self)411                self.unbind()412                raise LDAPBindError(error)413 414    def __str__(self):415        s = [416            str(self.server) if self.server else 'None',417            'user: ' + str(self.user),418            'lazy' if self.lazy else 'not lazy',419            'unbound' if not self.bound else ('deferred bind' if self._deferred_bind else 'bound'),420            'closed' if self.closed else ('deferred open' if self._deferred_open else 'open'),421            _format_socket_endpoints(self.socket),422            'tls not started' if not self.tls_started else('deferred start_tls' if self._deferred_start_tls else 'tls started'),423            'listening' if self.listening else 'not listening',424            self.strategy.__class__.__name__ if hasattr(self, 'strategy') else 'No strategy',425            'internal decoder' if self.fast_decoder else 'pyasn1 decoder'426        ]427        return ' - '.join(s)428 429    def __repr__(self):430        conf_default_pool_name = get_config_parameter('DEFAULT_THREADED_POOL_NAME')431        if self.server_pool:432            r = 'Connection(server={0.server_pool!r}'.format(self)433        else:434            r = 'Connection(server={0.server!r}'.format(self)435        r += '' if self.user is None else ', user={0.user!r}'.format(self)436        r += '' if self.password is None else ', password={0.password!r}'.format(self)437        r += '' if self.auto_bind is None else ', auto_bind={0.auto_bind!r}'.format(self)438        r += '' if self.version is None else ', version={0.version!r}'.format(self)439        r += '' if self.authentication is None else ', authentication={0.authentication!r}'.format(self)440        r += '' if self.strategy_type is None else ', client_strategy={0.strategy_type!r}'.format(self)441        r += '' if self.auto_referrals is None else ', auto_referrals={0.auto_referrals!r}'.format(self)442        r += '' if self.sasl_mechanism is None else ', sasl_mechanism={0.sasl_mechanism!r}'.format(self)443        r += '' if self.sasl_credentials is None else ', sasl_credentials={0.sasl_credentials!r}'.format(self)444        r += '' if self.check_names is None else ', check_names={0.check_names!r}'.format(self)445        r += '' if self.usage is None else (', collect_usage=' + ('True' if self.usage else 'False'))446        r += '' if self.read_only is None else ', read_only={0.read_only!r}'.format(self)447        r += '' if self.lazy is None else ', lazy={0.lazy!r}'.format(self)448        r += '' if self.raise_exceptions is None else ', raise_exceptions={0.raise_exceptions!r}'.format(self)449        r += '' if (self.pool_name is None or self.pool_name == conf_default_pool_name) else ', pool_name={0.pool_name!r}'.format(self)450        r += '' if self.pool_size is None else ', pool_size={0.pool_size!r}'.format(self)451        r += '' if self.pool_lifetime is None else ', pool_lifetime={0.pool_lifetime!r}'.format(self)452        r += '' if self.pool_keepalive is None else ', pool_keepalive={0.pool_keepalive!r}'.format(self)453        r += '' if self.cred_store is None else (', cred_store=' + repr(self.cred_store))454        r += '' if self.fast_decoder is None else (', fast_decoder=' + ('True' if self.fast_decoder else 'False'))455        r += '' if self.auto_range is None else (', auto_range=' + ('True' if self.auto_range else 'False'))456        r += '' if self.receive_timeout is None else ', receive_timeout={0.receive_timeout!r}'.format(self)457        r += '' if self.empty_attributes is None else (', return_empty_attributes=' + ('True' if self.empty_attributes else 'False'))458        r += '' if self.auto_encode is None else (', auto_encode=' + ('True' if self.auto_encode else 'False'))459        r += '' if self.auto_escape is None else (', auto_escape=' + ('True' if self.auto_escape else 'False'))460        r += '' if self.use_referral_cache is None else (', use_referral_cache=' + ('True' if self.use_referral_cache else 'False'))461        r += ')'462 463        return r464 465    def repr_with_sensitive_data_stripped(self):466        conf_default_pool_name = get_config_parameter('DEFAULT_THREADED_POOL_NAME')467        if self.server_pool:468            r = 'Connection(server={0.server_pool!r}'.format(self)469        else:470            r = 'Connection(server={0.server!r}'.format(self)471        r += '' if self.user is None else ', user={0.user!r}'.format(self)472        r += '' if self.password is None else ", password='{0}'".format('<stripped %d characters of sensitive data>' % len(self.password))473        r += '' if self.auto_bind is None else ', auto_bind={0.auto_bind!r}'.format(self)474        r += '' if self.version is None else ', version={0.version!r}'.format(self)475        r += '' if self.authentication is None else ', authentication={0.authentication!r}'.format(self)476        r += '' if self.strategy_type is None else ', client_strategy={0.strategy_type!r}'.format(self)477        r += '' if self.auto_referrals is None else ', auto_referrals={0.auto_referrals!r}'.format(self)478        r += '' if self.sasl_mechanism is None else ', sasl_mechanism={0.sasl_mechanism!r}'.format(self)479        if self.sasl_mechanism == DIGEST_MD5:480            r += '' if self.sasl_credentials is None else ", sasl_credentials=({0!r}, {1!r}, '{2}', {3!r})".format(self.sasl_credentials[0], self.sasl_credentials[1], '*' * len(self.sasl_credentials[2]), self.sasl_credentials[3])481        else:482            r += '' if self.sasl_credentials is None else ', sasl_credentials={0.sasl_credentials!r}'.format(self)483        r += '' if self.check_names is None else ', check_names={0.check_names!r}'.format(self)484        r += '' if self.usage is None else (', collect_usage=' + 'True' if self.usage else 'False')485        r += '' if self.read_only is None else ', read_only={0.read_only!r}'.format(self)486        r += '' if self.lazy is None else ', lazy={0.lazy!r}'.format(self)487        r += '' if self.raise_exceptions is None else ', raise_exceptions={0.raise_exceptions!r}'.format(self)488        r += '' if (self.pool_name is None or self.pool_name == conf_default_pool_name) else ', pool_name={0.pool_name!r}'.format(self)489        r += '' if self.pool_size is None else ', pool_size={0.pool_size!r}'.format(self)490        r += '' if self.pool_lifetime is None else ', pool_lifetime={0.pool_lifetime!r}'.format(self)491        r += '' if self.pool_keepalive is None else ', pool_keepalive={0.pool_keepalive!r}'.format(self)492        r += '' if self.cred_store is None else (', cred_store=' + repr(self.cred_store))493        r += '' if self.fast_decoder is None else (', fast_decoder=' + 'True' if self.fast_decoder else 'False')494        r += '' if self.auto_range is None else (', auto_range=' + ('True' if self.auto_range else 'False'))495        r += '' if self.receive_timeout is None else ', receive_timeout={0.receive_timeout!r}'.format(self)496        r += '' if self.empty_attributes is None else (', return_empty_attributes=' + 'True' if self.empty_attributes else 'False')497        r += '' if self.auto_encode is None else (', auto_encode=' + ('True' if self.auto_encode else 'False'))498        r += '' if self.auto_escape is None else (', auto_escape=' + ('True' if self.auto_escape else 'False'))499        r += '' if self.use_referral_cache is None else (', use_referral_cache=' + ('True' if self.use_referral_cache else 'False'))500        r += ')'501 502        return r503 504    @property505    def stream(self):506        """Used by the LDIFProducer strategy to accumulate the ldif-change operations with a single LDIF header507        :return: reference to the response stream if defined in the strategy.508        """509        return self.strategy.get_stream() if self.strategy.can_stream else None510 511    @stream.setter512    def stream(self, value):513        with self.connection_lock:514            if self.strategy.can_stream:515                self.strategy.set_stream(value)516 517    @property518    def usage(self):519        """Usage statistics for the connection.520        :return: Usage object521        """522        if not self._usage:523            return None524        if self.strategy.pooled:  # update master connection usage from pooled connections525            self._usage.reset()526            for worker in self.strategy.pool.workers:527                self._usage += worker.connection.usage528            self._usage += self.strategy.pool.terminated_usage529        return self._usage530 531    def __enter__(self):532        with self.connection_lock:533            self._context_state.append((self.bound, self.closed))  # save status out of context as a tuple in a list534            if self.auto_bind != AUTO_BIND_NONE:535                if self.auto_bind == AUTO_BIND_DEFAULT:536                    self.auto_bind = AUTO_BIND_NO_TLS537                if self.closed:538                    self.open()539                if not self.bound:540                    if not self.bind():541                        raise LDAPBindError('unable to bind')542 543            return self544 545    def __exit__(self, exc_type, exc_val, exc_tb):546        with self.connection_lock:547            context_bound, context_closed = self._context_state.pop()548            if (not context_bound and self.bound) or self.stream:  # restore status prior to entering context549                try:550                    self.unbind()551                except LDAPExceptionError:552                    pass553 554            if not context_closed and self.closed:555                self.open()556 557            if exc_type is not None:558                if log_enabled(ERROR):559                    log(ERROR, '%s for <%s>', exc_type, self)560                return False  # re-raise LDAPExceptionError561 562    def bind(self,563             read_server_info=True,564             controls=None):565        """Bind to ldap Server with the authentication method and the user defined in the connection566 567        :param read_server_info: reads info from server568        :param controls: LDAP controls to send along with the bind operation569        :type controls: list of tuple570        :return: bool571 572        """573        if log_enabled(BASIC):574            log(BASIC, 'start BIND operation via <%s>', self)575        self.last_error = None576        with self.connection_lock:577            if self.lazy and not self._executing_deferred:578                if self.strategy.pooled:579                    self.strategy.validate_bind(controls)580                self._deferred_bind = True581                self._bind_controls = controls582                self.bound = True583                if log_enabled(BASIC):584                    log(BASIC, 'deferring bind for <%s>', self)585            else:586                self._deferred_bind = False587                self._bind_controls = None588                if self.closed:  # try to open connection if closed589                    self.open(read_server_info=False)590                if self.authentication == ANONYMOUS:591                    if log_enabled(PROTOCOL):592                        log(PROTOCOL, 'performing anonymous BIND for <%s>', self)593                    if not self.strategy.pooled:594                        request = bind_operation(self.version, self.authentication, self.user, '', auto_encode=self.auto_encode)595                        if log_enabled(PROTOCOL):596                            log(PROTOCOL, 'anonymous BIND request <%s> sent via <%s>', bind_request_to_dict(request), self)597                        response = self.post_send_single_response(self.send('bindRequest', request, controls))598                    else:599                        response = self.strategy.validate_bind(controls)  # only for REUSABLE600                elif self.authentication == SIMPLE:601                    if log_enabled(PROTOCOL):602                        log(PROTOCOL, 'performing simple BIND for <%s>', self)603                    if not self.strategy.pooled:604                        request = bind_operation(self.version, self.authentication, self.user, self.password, auto_encode=self.auto_encode)605                        if log_enabled(PROTOCOL):606                            log(PROTOCOL, 'simple BIND request <%s> sent via <%s>', bind_request_to_dict(request), self)607                        response = self.post_send_single_response(self.send('bindRequest', request, controls))608                    else:609                        response = self.strategy.validate_bind(controls)  # only for REUSABLE610                elif self.authentication == SASL:611                    if self.sasl_mechanism in SASL_AVAILABLE_MECHANISMS:612                        if log_enabled(PROTOCOL):613                            log(PROTOCOL, 'performing SASL BIND for <%s>', self)614                        if not self.strategy.pooled:615                            response = self.do_sasl_bind(controls)616                        else:617                            response = self.strategy.validate_bind(controls)  # only for REUSABLE618                    else:619                        self.last_error = 'requested SASL mechanism not supported'620                        if log_enabled(ERROR):621                            log(ERROR, '%s for <%s>', self.last_error, self)622                        raise LDAPSASLMechanismNotSupportedError(self.last_error)623                elif self.authentication == NTLM:624                    if self.user and self.password and len(self.user.split('\\')) == 2:625                        if log_enabled(PROTOCOL):626                            log(PROTOCOL, 'performing NTLM BIND for <%s>', self)627                        if not self.strategy.pooled:628                            response = self.do_ntlm_bind(controls)629                        else:630                            response = self.strategy.validate_bind(controls)  # only for REUSABLE631                    else:  # user or password missing632                        self.last_error = 'NTLM needs domain\\username and a password'633                        if log_enabled(ERROR):634                            log(ERROR, '%s for <%s>', self.last_error, self)635                        raise LDAPUnknownAuthenticationMethodError(self.last_error)636                else:637                    self.last_error = 'unknown authentication method'638                    if log_enabled(ERROR):639                        log(ERROR, '%s for <%s>', self.last_error, self)640                    raise LDAPUnknownAuthenticationMethodError(self.last_error)641 642                if not self.strategy.sync and not self.strategy.pooled and self.authentication not in (SASL, NTLM):  # get response if asynchronous except for SASL and NTLM that return the bind result even for asynchronous strategy643                    _, result = self.get_response(response)644                    if log_enabled(PROTOCOL):645                        log(PROTOCOL, 'async BIND response id <%s> received via <%s>', result, self)646                elif self.strategy.sync:647                    result = self.result648                    if log_enabled(PROTOCOL):649                        log(PROTOCOL, 'BIND response <%s> received via <%s>', result, self)650                elif self.strategy.pooled or self.authentication in (SASL, NTLM):  # asynchronous SASL and NTLM or reusable strtegy get the bind result synchronously651                    result = response652                else:653                    self.last_error = 'unknown authentication method'654                    if log_enabled(ERROR):655                        log(ERROR, '%s for <%s>', self.last_error, self)656                    raise LDAPUnknownAuthenticationMethodError(self.last_error)657 658                if result is None:659                    # self.bound = True if self.strategy_type == REUSABLE else False660                    self.bound = False661                elif result is True:662                    self.bound = True663                elif result is False:664                    self.bound = False665                else:666                    self.bound = True if result['result'] == RESULT_SUCCESS else False667                    if not self.bound and result and result['description'] and not self.last_error:668                        self.last_error = result['description']669 670                if read_server_info and self.bound:671                    self.refresh_server_info()672            self._entries = []673 674            if log_enabled(BASIC):675                log(BASIC, 'done BIND operation, result <%s>', self.bound)676 677            return self._prepare_return_value(self.bound, self.result)678 679    def rebind(self,680               user=None,681               password=None,682               authentication=None,683               sasl_mechanism=None,684               sasl_credentials=None,685               read_server_info=True,686               controls=None687               ):688 689        if log_enabled(BASIC):690            log(BASIC, 'start (RE)BIND operation via <%s>', self)691        self.last_error = None692        with self.connection_lock:693            if user:694                self.user = user695            if password is not None:696                self.password = password697            if not authentication and user:698                self.authentication = SIMPLE699            if authentication in [SIMPLE, ANONYMOUS, SASL, NTLM]:700                self.authentication = authentication701            elif authentication is not None:702                self.last_error = 'unknown authentication method'703                if log_enabled(ERROR):704                    log(ERROR, '%s for <%s>', self.last_error, self)705                raise LDAPUnknownAuthenticationMethodError(self.last_error)706            if sasl_mechanism:707                self.sasl_mechanism = sasl_mechanism708            if sasl_credentials:709                self.sasl_credentials = sasl_credentials710 711            # if self.authentication == SIMPLE and self.user and self.check_names:712            #     self.user = safe_dn(self.user)713            #     if log_enabled(EXTENDED):714            #         log(EXTENDED, 'user name sanitized to <%s> for rebind via <%s>', self.user, self)715 716            if not self.strategy.pooled:717                try:718                    return self.bind(read_server_info, controls)719                except LDAPSocketReceiveError:720                    self.last_error = 'Unable to rebind as a different user, furthermore the server abruptly closed the connection'721                    if log_enabled(ERROR):722                        log(ERROR, '%s for <%s>', self.last_error, self)723                    raise LDAPBindError(self.last_error)724            else:725                self.strategy.pool.rebind_pool()726                return self._prepare_return_value(True, self.result)727 728    def unbind(self,729               controls=None):730        """Unbind the connected user. Unbind implies closing session as per RFC4511 (4.3)731 732        :param controls: LDAP controls to send along with the bind operation733 734        """735        if log_enabled(BASIC):736            log(BASIC, 'start UNBIND operation via <%s>', self)737 738        if self.use_referral_cache:739            self.strategy.unbind_referral_cache()740 741        self.last_error = None742        with self.connection_lock:743            if self.lazy and not self._executing_deferred and (self._deferred_bind or self._deferred_open):  # _clear deferred status744                self.strategy.close()745                self._deferred_open = False746                self._deferred_bind = False747                self._deferred_start_tls = False748            elif not self.closed:749                request = unbind_operation()750                if log_enabled(PROTOCOL):751                    log(PROTOCOL, 'UNBIND request sent via <%s>', self)752                self.send('unbindRequest', request, controls)753                self.strategy.close()754 755            if log_enabled(BASIC):756                log(BASIC, 'done UNBIND operation, result <%s>', True)757 758            return self._prepare_return_value(True)759 760    def search(self,761               search_base,762               search_filter,763               search_scope=SUBTREE,764               dereference_aliases=DEREF_ALWAYS,765               attributes=None,766               size_limit=0,767               time_limit=0,768               types_only=False,769               get_operational_attributes=False,770               controls=None,771               paged_size=None,772               paged_criticality=False,773               paged_cookie=None,774               auto_escape=None):775        """776        Perform an ldap search:777 778        - If attributes is empty noRFC2696 with the specified size779        - If paged is 0 and cookie is present the search is abandoned on780          server attribute is returned781        - If attributes is ALL_ATTRIBUTES all attributes are returned782        - If paged_size is an int greater than 0 a simple paged search783          is tried as described in784        - Cookie is an opaque string received in the last paged search785          and must be used on the next paged search response786        - If lazy == True open and bind will be deferred until another787          LDAP operation is performed788        - If mssing_attributes == True then an attribute not returned by the server is set to None789        - If auto_escape is set it overrides the Connection auto_escape790        """791        conf_attributes_excluded_from_check = [v.lower() for v in get_config_parameter('ATTRIBUTES_EXCLUDED_FROM_CHECK')]792        if log_enabled(BASIC):793            log(BASIC, 'start SEARCH operation via <%s>', self)794 795        if self.check_names and search_base:796            search_base = safe_dn(search_base)797            if log_enabled(EXTENDED):798                log(EXTENDED, 'search base sanitized to <%s> for SEARCH operation via <%s>', search_base, self)799 800        with self.connection_lock:801            self._fire_deferred()802            if not attributes:803                attributes = [NO_ATTRIBUTES]804            elif attributes == ALL_ATTRIBUTES:805                attributes = [ALL_ATTRIBUTES]806 807            if isinstance(attributes, STRING_TYPES):808                attributes = [attributes]809 810            if get_operational_attributes and isinstance(attributes, list):811                attributes.append(ALL_OPERATIONAL_ATTRIBUTES)812            elif get_operational_attributes and isinstance(attributes, tuple):813                attributes += (ALL_OPERATIONAL_ATTRIBUTES, )  # concatenate tuple814 815            if isinstance(paged_size, int):816                if log_enabled(PROTOCOL):817                    log(PROTOCOL, 'performing paged search for %d items with cookie <%s> for <%s>', paged_size, escape_bytes(paged_cookie), self)818 819                if controls is None:820                    controls = []821                else:822                    # Copy the controls to prevent modifying the original object823                    controls = list(controls)824                controls.append(paged_search_control(paged_criticality, paged_size, paged_cookie))825 826            if self.server and self.server.schema and self.check_names:827                for attribute_name in attributes:828                    if ';' in attribute_name:  # remove tags829                        attribute_name_to_check = attribute_name.split(';')[0]830                    else:831                        attribute_name_to_check = attribute_name832                    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:833                        self.last_error = 'invalid attribute type ' + attribute_name_to_check834                        if log_enabled(ERROR):835                            log(ERROR, '%s for <%s>', self.last_error, self)836                        raise LDAPAttributeError(self.last_error)837 838            request = search_operation(search_base,839                                       search_filter,840                                       search_scope,841                                       dereference_aliases,842                                       attributes,843                                       size_limit,844                                       time_limit,845                                       types_only,846                                       self.auto_escape if auto_escape is None else auto_escape,847                                       self.auto_encode,848                                       self.server.schema if self.server else None,849                                       validator=self.server.custom_validator,850                                       check_names=self.check_names)851            if log_enabled(PROTOCOL):852                log(PROTOCOL, 'SEARCH request <%s> sent via <%s>', search_request_to_dict(request), self)853            response = self.post_send_search(self.send('searchRequest', request, controls))854            self._entries = []855 856            if isinstance(response, int):  # asynchronous strategy857                return_value = response858                if log_enabled(PROTOCOL):859                    log(PROTOCOL, 'async SEARCH response id <%s> received via <%s>', return_value, self)860            else:861                return_value = True if self.result['type'] == 'searchResDone' and len(response) > 0 else False862                if not return_value and self.result['result'] not in [RESULT_SUCCESS] and not self.last_error:863                    self.last_error = self.result['description']864 865                if log_enabled(PROTOCOL):866                    for entry in response:867                        if entry['type'] == 'searchResEntry':868                            log(PROTOCOL, 'SEARCH response entry <%s> received via <%s>', entry, self)869                        elif entry['type'] == 'searchResRef':870                            log(PROTOCOL, 'SEARCH response reference <%s> received via <%s>', entry, self)871 872            if log_enabled(BASIC):873                log(BASIC, 'done SEARCH operation, result <%s>', return_value)874 875            return self._prepare_return_value(return_value, response=True)876 877    def compare(self,878                dn,879                attribute,880                value,881                controls=None):882        """883        Perform a compare operation884        """885        conf_attributes_excluded_from_check = [v.lower() for v in get_config_parameter('ATTRIBUTES_EXCLUDED_FROM_CHECK')]886 887        if log_enabled(BASIC):888            log(BASIC, 'start COMPARE operation via <%s>', self)889        self.last_error = None890        if self.check_names:891            dn = safe_dn(dn)892            if log_enabled(EXTENDED):893                log(EXTENDED, 'dn sanitized to <%s> for COMPARE operation via <%s>', dn, self)894 895        if self.server and self.server.schema and self.check_names:896            if ';' in attribute:  # remove tags for checking897                attribute_name_to_check = attribute.split(';')[0]898            else:899                attribute_name_to_check = attribute900 901            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:902                self.last_error = 'invalid attribute type ' + attribute_name_to_check903                if log_enabled(ERROR):904                    log(ERROR, '%s for <%s>', self.last_error, self)905                raise LDAPAttributeError(self.last_error)906 907        if isinstance(value, SEQUENCE_TYPES):  # value can't be a sequence908            self.last_error = 'value cannot be a sequence'909            if log_enabled(ERROR):910                log(ERROR, '%s for <%s>', self.last_error, self)911            raise LDAPInvalidValueError(self.last_error)912 913        with self.connection_lock:914            self._fire_deferred()915            request = compare_operation(dn, attribute, value, 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)916            if log_enabled(PROTOCOL):917                log(PROTOCOL, 'COMPARE request <%s> sent via <%s>', compare_request_to_dict(request), self)918            response = self.post_send_single_response(self.send('compareRequest', request, controls))919            self._entries = []920            if isinstance(response, int):921                return_value = response922                if log_enabled(PROTOCOL):923                    log(PROTOCOL, 'async COMPARE response id <%s> received via <%s>', return_value, self)924            else:925                return_value = True if self.result['type'] == 'compareResponse' and self.result['result'] == RESULT_COMPARE_TRUE else False926                if not return_value and self.result['result'] not in [RESULT_COMPARE_TRUE, RESULT_COMPARE_FALSE] and not self.last_error:927                    self.last_error = self.result['description']928 929                if log_enabled(PROTOCOL):930                    log(PROTOCOL, 'COMPARE response <%s> received via <%s>', response, self)931 932            if log_enabled(BASIC):933                log(BASIC, 'done COMPARE operation, result <%s>', return_value)934 935            return self._prepare_return_value(return_value)936 937    def add(self,938            dn,939            object_class=None,940            attributes=None,941            controls=None):942        """943        Add dn to the DIT, object_class is None, a class name or a list944        of class names.945 946        Attributes is a dictionary in the form 'attr': 'val' or 'attr':947        ['val1', 'val2', ...] for multivalued attributes948        """949        conf_attributes_excluded_from_check = [v.lower() for v in get_config_parameter('ATTRIBUTES_EXCLUDED_FROM_CHECK')]950        conf_classes_excluded_from_check = [v.lower() for v in get_config_parameter('CLASSES_EXCLUDED_FROM_CHECK')]951        if log_enabled(BASIC):952            log(BASIC, 'start ADD operation via <%s>', self)953        self.last_error = None954        _attributes = deepcopy(attributes)  # dict could change when adding objectClass values955        if self.check_names:956            dn = safe_dn(dn)957            if log_enabled(EXTENDED):958                log(EXTENDED, 'dn sanitized to <%s> for ADD operation via <%s>', dn, self)959 960        with self.connection_lock:961            self._fire_deferred()962            attr_object_class = []963            if object_class is None:964                parm_object_class = []965            else:966                parm_object_class = list(object_class) if isinstance(object_class, SEQUENCE_TYPES) else [object_class]967 968            object_class_attr_name = ''969            if _attributes:970                for attr in _attributes:971                    if attr.lower() == 'objectclass':972                        object_class_attr_name = attr973                        attr_object_class = list(_attributes[object_class_attr_name]) if isinstance(_attributes[object_class_attr_name], SEQUENCE_TYPES) else [_attributes[object_class_attr_name]]974                        break975            else:976                _attributes = dict()977 978            if not object_class_attr_name:979                object_class_attr_name = 'objectClass'980 981            attr_object_class = [to_unicode(object_class) for object_class in attr_object_class]  # converts objectclass to unicode in case of bytes value982            _attributes[object_class_attr_name] = reduce(lambda x, y: x + [y] if y not in x else x, parm_object_class + attr_object_class, [])  # remove duplicate ObjectClasses983 984            if not _attributes[object_class_attr_name]:985                self.last_error = 'objectClass attribute is mandatory'986                if log_enabled(ERROR):987                    log(ERROR, '%s for <%s>', self.last_error, self)988                raise LDAPObjectClassError(self.last_error)989 990            if self.server and self.server.schema and self.check_names:991                for object_class_name in _attributes[object_class_attr_name]:992                    if object_class_name.lower() not in conf_classes_excluded_from_check and object_class_name not in self.server.schema.object_classes:993                        self.last_error = 'invalid object class ' + str(object_class_name)994                        if log_enabled(ERROR):995                            log(ERROR, '%s for <%s>', self.last_error, self)996                        raise LDAPObjectClassError(self.last_error)997 998                for attribute_name in _attributes:999                    if ';' in attribute_name:  # remove tags for checking1000                        attribute_name_to_check = attribute_name.split(';')[0]1001                    else:1002                        attribute_name_to_check = attribute_name1003 1004                    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:1005                        self.last_error = 'invalid attribute type ' + attribute_name_to_check1006                        if log_enabled(ERROR):1007                            log(ERROR, '%s for <%s>', self.last_error, self)1008                        raise LDAPAttributeError(self.last_error)1009 1010            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)1011            if log_enabled(PROTOCOL):1012                log(PROTOCOL, 'ADD request <%s> sent via <%s>', add_request_to_dict(request), self)1013            response = self.post_send_single_response(self.send('addRequest', request, controls))1014            self._entries = []1015 1016            if isinstance(response, STRING_TYPES + (int, )):1017                return_value = response1018                if log_enabled(PROTOCOL):1019                    log(PROTOCOL, 'async ADD response id <%s> received via <%s>', return_value, self)1020            else:1021                if log_enabled(PROTOCOL):1022                    log(PROTOCOL, 'ADD response <%s> received via <%s>', response, self)1023                return_value = True if self.result['type'] == 'addResponse' and self.result['result'] == RESULT_SUCCESS else False1024                if not return_value and self.result['result'] not in [RESULT_SUCCESS] and not self.last_error:1025                    self.last_error = self.result['description']1026 1027            if log_enabled(BASIC):1028                log(BASIC, 'done ADD operation, result <%s>', return_value)1029 1030            return self._prepare_return_value(return_value)1031 1032    def delete(self,1033               dn,1034               controls=None):1035        """1036        Delete the entry identified by the DN from the DIB.1037        """1038        if log_enabled(BASIC):1039            log(BASIC, 'start DELETE operation via <%s>', self)1040        self.last_error = None1041        if self.check_names:1042            dn = safe_dn(dn)1043            if log_enabled(EXTENDED):1044                log(EXTENDED, 'dn sanitized to <%s> for DELETE operation via <%s>', dn, self)1045 1046        with self.connection_lock:1047            self._fire_deferred()1048            if self.read_only:1049                self.last_error = 'connection is read-only'1050                if log_enabled(ERROR):1051                    log(ERROR, '%s for <%s>', self.last_error, self)1052                raise LDAPConnectionIsReadOnlyError(self.last_error)1053 1054            request = delete_operation(dn)1055            if log_enabled(PROTOCOL):1056                log(PROTOCOL, 'DELETE request <%s> sent via <%s>', delete_request_to_dict(request), self)1057            response = self.post_send_single_response(self.send('delRequest', request, controls))1058            self._entries = []1059 1060            if isinstance(response, STRING_TYPES + (int, )):1061                return_value = response1062                if log_enabled(PROTOCOL):1063                    log(PROTOCOL, 'async DELETE response id <%s> received via <%s>', return_value, self)1064            else:1065                if log_enabled(PROTOCOL):1066                    log(PROTOCOL, 'DELETE response <%s> received via <%s>', response, self)1067                return_value = True if self.result['type'] == 'delResponse' and self.result['result'] == RESULT_SUCCESS else False1068                if not return_value and self.result['result'] not in [RESULT_SUCCESS] and not self.last_error:1069                    self.last_error = self.result['description']1070 1071            if log_enabled(BASIC):1072                log(BASIC, 'done DELETE operation, result <%s>', return_value)1073 1074            return self._prepare_return_value(return_value)1075 1076    def modify(self,1077               dn,1078               changes,1079               controls=None):1080        """1081        Modify attributes of entry1082 1083        - changes is a dictionary in the form {'attribute1': change), 'attribute2': [change, change, ...], ...}1084        - change is (operation, [value1, value2, ...])1085        - operation is 0 (MODIFY_ADD), 1 (MODIFY_DELETE), 2 (MODIFY_REPLACE), 3 (MODIFY_INCREMENT)1086        """1087        conf_attributes_excluded_from_check = [v.lower() for v in get_config_parameter('ATTRIBUTES_EXCLUDED_FROM_CHECK')]1088 1089        if log_enabled(BASIC):1090            log(BASIC, 'start MODIFY operation via <%s>', self)1091        self.last_error = None1092        if self.check_names:1093            dn = safe_dn(dn)1094            if log_enabled(EXTENDED):1095                log(EXTENDED, 'dn sanitized to <%s> for MODIFY operation via <%s>', dn, self)1096 1097        with self.connection_lock:1098            self._fire_deferred()1099            if self.read_only:1100                self.last_error = 'connection is read-only'1101                if log_enabled(ERROR):1102                    log(ERROR, '%s for <%s>', self.last_error, self)1103                raise LDAPConnectionIsReadOnlyError(self.last_error)1104 1105            if not isinstance(changes, dict):1106                self.last_error = 'changes must be a dictionary'1107                if log_enabled(ERROR):1108                    log(ERROR, '%s for <%s>', self.last_error, self)1109                raise LDAPChangeError(self.last_error)1110 1111            if not changes:1112                self.last_error = 'no changes in modify request'1113                if log_enabled(ERROR):1114                    log(ERROR, '%s for <%s>', self.last_error, self)1115                raise LDAPChangeError(self.last_error)1116 1117            changelist = dict()1118            for attribute_name in changes:1119                if self.server and self.server.schema and self.check_names:1120                    if ';' in attribute_name:  # remove tags for checking1121                        attribute_name_to_check = attribute_name.split(';')[0]1122                    else:1123                        attribute_name_to_check = attribute_name1124 1125                    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:1126                        self.last_error = 'invalid attribute type ' + attribute_name_to_check1127                        if log_enabled(ERROR):1128                            log(ERROR, '%s for <%s>', self.last_error, self)1129                        raise LDAPAttributeError(self.last_error)1130                change = changes[attribute_name]1131                if isinstance(change, SEQUENCE_TYPES) and change[0] in [MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE, MODIFY_INCREMENT, 0, 1, 2, 3]:1132                    if len(change) != 2:1133                        self.last_error = 'malformed change'1134                        if log_enabled(ERROR):1135                            log(ERROR, '%s for <%s>', self.last_error, self)1136                        raise LDAPChangeError(self.last_error)1137 1138                    changelist[attribute_name] = [change]  # insert change in a list1139                else:1140                    for change_operation in change:1141                        if len(change_operation) != 2 or change_operation[0] not in [MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE, MODIFY_INCREMENT, 0, 1, 2, 3]:1142                            self.last_error = 'invalid change list'1143                            if log_enabled(ERROR):1144                                log(ERROR, '%s for <%s>', self.last_error, self)1145                            raise LDAPChangeError(self.last_error)1146                    changelist[attribute_name] = change1147            request = modify_operation(dn, changelist, 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)1148            if log_enabled(PROTOCOL):1149                log(PROTOCOL, 'MODIFY request <%s> sent via <%s>', modify_request_to_dict(request), self)1150            response = self.post_send_single_response(self.send('modifyRequest', request, controls))1151            self._entries = []1152 1153            if isinstance(response, STRING_TYPES + (int, )):1154                return_value = response1155                if log_enabled(PROTOCOL):1156                    log(PROTOCOL, 'async MODIFY response id <%s> received via <%s>', return_value, self)1157            else:1158                if log_enabled(PROTOCOL):1159                    log(PROTOCOL, 'MODIFY response <%s> received via <%s>', response, self)1160                return_value = True if self.result['type'] == 'modifyResponse' and self.result['result'] == RESULT_SUCCESS else False1161                if not return_value and self.result['result'] not in [RESULT_SUCCESS] and not self.last_error:1162                    self.last_error = self.result['description']1163 1164            if log_enabled(BASIC):1165                log(BASIC, 'done MODIFY operation, result <%s>', return_value)1166 1167            return self._prepare_return_value(return_value)1168 1169    def modify_dn(self,1170                  dn,1171                  relative_dn,1172                  delete_old_dn=True,1173                  new_superior=None,1174                  controls=None):1175        """1176        Modify DN of the entry or performs a move of the entry in the1177        DIT.1178        """1179        if log_enabled(BASIC):1180            log(BASIC, 'start MODIFY DN operation via <%s>', self)1181        self.last_error = None1182        if self.check_names:1183            dn = safe_dn(dn)1184            if log_enabled(EXTENDED):1185                log(EXTENDED, 'dn sanitized to <%s> for MODIFY DN operation via <%s>', dn, self)1186            relative_dn = safe_dn(relative_dn)1187            if log_enabled(EXTENDED):1188                log(EXTENDED, 'relative dn sanitized to <%s> for MODIFY DN operation via <%s>', relative_dn, self)1189 1190        with self.connection_lock:1191            self._fire_deferred()1192            if self.read_only:1193                self.last_error = 'connection is read-only'1194                if log_enabled(ERROR):1195                    log(ERROR, '%s for <%s>', self.last_error, self)1196                raise LDAPConnectionIsReadOnlyError(self.last_error)1197 1198            # if new_superior and not dn.startswith(relative_dn):  # as per RFC4511 (4.9)1199            #     self.last_error = 'DN cannot change while performing moving'1200            #     if log_enabled(ERROR):

Showing the first 1,200 of 1606 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai