From: Frank Brehm Date: Fri, 26 Jan 2024 16:03:11 +0000 (+0100) Subject: Rewriting lib/pp_admintools/app/ldap.py to one TCP session per request. X-Git-Tag: 1.0.0~1^2~33^2 X-Git-Url: https://git.uhu-banane.de/?a=commitdiff_plain;h=9835bbe93f4b0bd6a4f14d66b52ba84cb4bb4e25;p=pixelpark%2Fpp-admin-tools.git Rewriting lib/pp_admintools/app/ldap.py to one TCP session per request. --- diff --git a/lib/pp_admintools/app/ldap.py b/lib/pp_admintools/app/ldap.py index 9aecf53..b650ba2 100644 --- a/lib/pp_admintools/app/ldap.py +++ b/lib/pp_admintools/app/ldap.py @@ -37,6 +37,7 @@ from ldap3 import Connection, DSA, IP_V4_PREFERRED, SAFE_SYNC, Server from ldap3 import MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE from ldap3.core.exceptions import LDAPBindError from ldap3.core.exceptions import LDAPException +from ldap3.core.exceptions import LDAPSessionTerminatedByServerError from ldap3.core.exceptions import LDAPSocketOpenError from ldap3.core.exceptions import LDAPSocketReceiveError @@ -49,7 +50,7 @@ from ..config.ldap import DEFAULT_TIMEOUT from ..config.ldap import LdapConfiguration, LdapConnectionInfo from ..xlate import XLATOR, format_list -__version__ = '0.12.3' +__version__ = '1.0.0' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -104,6 +105,13 @@ class LDAPParseError(FatalLDAPError): pass +# ============================================================================= +class LdapSessionError(FatalLDAPError): + """Error in session handling with LDAP.""" + + pass + + # ============================================================================= class PasswordFileOptionAction(argparse.Action): """Argparse action for a password file.""" @@ -181,6 +189,7 @@ class BaseLdapApplication(BaseDPXApplication): apply_default_ldap_instance_if_not_given = True default_default_ldap_instance = 'default' default_wait_on_read_error = 1 + default_single_session = False # pattern_re_ldap_dn = ( # '^([a-z][a-z0-9-]*)=(?![ #])(((?![\\="+,;<>]).)|(\\[ \\#="+,;<>])|(\\[a-f0-9][a-f0-9]))*' @@ -267,13 +276,15 @@ class BaseLdapApplication(BaseDPXApplication): self, appname=None, verbose=0, version=GLOBAL_VERSION, base_dir=None, cfg_class=LdapConfiguration, initialized=False, usage=None, description=None, argparse_epilog=None, argparse_prefix_chars='-', env_prefix=None, - config_dir=DEFAULT_CONFIG_DIR, wait_on_read_error=None): + config_dir=DEFAULT_CONFIG_DIR, wait_on_read_error=None, + single_session=None): """Contrict the application object.""" self._password_file = None self.ldap_instances = [] self.ldap_server = {} self.ldap_connection = {} self._wait_on_read_error = self.default_wait_on_read_error + self._single_session = self.default_single_session super(BaseLdapApplication, self).__init__( appname=appname, verbose=verbose, version=version, base_dir=base_dir, @@ -285,6 +296,9 @@ class BaseLdapApplication(BaseDPXApplication): if wait_on_read_error: self.wait_on_read_error = wait_on_read_error + if single_session is not None: + self.single_session = single_session + # ----------------------------------------------------------- @property def password_file(self): @@ -328,6 +342,24 @@ class BaseLdapApplication(BaseDPXApplication): raise ValueError(msg) self._wait_on_read_error = val + # ----------------------------------------------------------- + @property + def single_session(self): + """ + Execute all LDAP operations in one single TCP session. + + Otherwise each read or write operation will be executed + in a separate TCP session. + """ + return self._single_session + + @single_session.setter + def single_session(self, value): + if value is None: + self._single_session = self.default_single_session + return + self._single_session = to_bool(value) + # ------------------------------------------------------------------------- def as_dict(self, short=True): """ @@ -344,6 +376,7 @@ class BaseLdapApplication(BaseDPXApplication): res['default_default_ldap_instance'] = self.default_default_ldap_instance res['pattern_re_ldap_dn'] = self.pattern_re_ldap_dn res['password_file'] = self.password_file + res['single_session'] = self.single_session res['show_cmdline_ldap_timeout'] = self.show_cmdline_ldap_timeout res['use_default_ldap_connection'] = self.use_default_ldap_connection res['use_multiple_ldap_connections'] = self.use_multiple_ldap_connections @@ -756,17 +789,18 @@ class BaseLdapApplication(BaseDPXApplication): LOG.debug(_('Preparations ...')) super(BaseLdapApplication, self).pre_run() - LOG.debug(_('Open all necessary LDAP connections ...')) + if self.single_session: + LOG.debug(_('Open all necessary LDAP connections ...')) - for inst in self.ldap_instances: - self.connect_instance(inst) + for inst in self.ldap_instances: + self.connect_instance(inst, single=True) # ------------------------------------------------------------------------- - def connect_instance(self, inst): + def connect_instance(self, inst, single=False): """Connect to the given LDAP instance.""" connect_info = self.cfg.ldap_connection[inst] - ldap_server = self.get_ldap_server_obj(inst) + ldap_server = self.get_ldap_server_obj(inst, single=single) self.ldap_server[inst] = ldap_server if not connect_info.bind_pw: @@ -775,20 +809,26 @@ class BaseLdapApplication(BaseDPXApplication): inst=self.colored(connect_info.url, 'CYAN')) + ' ' connect_info.bind_pw = self.get_password(first_prompt, may_empty=False, repeat=False) - ldap_connection = self.connect_to_ldap_server(ldap_server, inst) + ldap_connection = self.connect_to_ldap_server(ldap_server, inst, single=single) self.ldap_connection[inst] = ldap_connection - if self.verbose > 2: + min_verb_level = 3 + if single: + min_verb_level = 2 + if self.verbose > min_verb_level: msg = _('Info about LDAP server {}:').format(connect_info.url) msg += ' ' + repr(ldap_connection) LOG.debug(msg) # ------------------------------------------------------------------------- - def get_ldap_server_obj(self, inst): + def get_ldap_server_obj(self, inst, single=False): """Return the ldap3-Server object for the given instance.""" connect_info = self.cfg.ldap_connection[inst] - if self.verbose > 2: + min_verb_level = 3 + if single: + min_verb_level = 2 + if self.verbose > min_verb_level: msg = _('Trying to get LDAP server object for {} ...').format(connect_info.url) LOG.debug(msg) @@ -804,20 +844,20 @@ class BaseLdapApplication(BaseDPXApplication): server_opts['get_info'] = DSA server_opts['mode'] = IP_V4_PREFERRED server_opts['connect_timeout'] = self.cfg.ldap_timeout - if self.verbose > 2: + if self.verbose > min_verb_level: msg = _('Connect options to server {!r}:').format(connect_info.url) msg += ' ' + pp(server_opts) LOG.debug(msg) ldap_server = Server(connect_info.host, **server_opts) - if self.verbose > 2: + if self.verbose > min_verb_level: LOG.debug(_('LDAP server {s}: {re}').format(s=ldap_server, re=repr(ldap_server))) return ldap_server # ------------------------------------------------------------------------- - def connect_to_ldap_server(self, ldap_server, inst, bind_dn=None, bind_pw=None): + def connect_to_ldap_server(self, ldap_server, inst, bind_dn=None, bind_pw=None, single=False): """Connect to the given LDAP server.""" connect_info = self.cfg.ldap_connection[inst] if not bind_dn: @@ -825,7 +865,11 @@ class BaseLdapApplication(BaseDPXApplication): if not bind_pw: bind_pw = connect_info.bind_pw - if self.verbose > 1: + min_verb_level = 2 + if single: + min_verb_level = 1 + + if self.verbose > min_verb_level: msg = _('Connecting to LDAP server {url} as {dn!r} ...').format( url=connect_info.url, dn=bind_dn) LOG.debug(msg) @@ -863,24 +907,29 @@ class BaseLdapApplication(BaseDPXApplication): LOG.debug(_('Disconnecting from all remaining LDAP instances ...')) for inst in self.ldap_instances: - self.disconnect_instance(inst) + self.disconnect_instance(inst, single=True) # ------------------------------------------------------------------------- - def disconnect_instance(self, inst): + def disconnect_instance(self, inst, single=False): """Disconnect from the given instance.""" connect_info = self.cfg.ldap_connection[inst] + min_verb_level = 2 + if single: + min_verb_level = 1 + if inst in self.ldap_connection: ldap_connection = self.ldap_connection[inst] - if self.verbose > 1: + if self.verbose > min_verb_level: LOG.debug(_('Unbinding from LDAP server {!r} ...').format(connect_info.url)) ldap_connection.unbind() ldap_connection = None del self.ldap_connection[inst] - if inst in self.ldap_server: - if self.verbose > 1: - LOG.debug(_('Disconnecting from LDAP server {!r} ...').format(connect_info.url)) + if inst in self.ldap_server and single: + if self.verbose > min_verb_level: + LOG.debug(_('Removing LDAP server connection data {!r} ...').format( + connect_info.url)) del self.ldap_server[inst] # ------------------------------------------------------------------------- @@ -893,7 +942,7 @@ class BaseLdapApplication(BaseDPXApplication): with the resulting attributes as values. """ connect_info = self.cfg.ldap_connection[inst] - ldap = self.ldap_connection[inst] + if scope is None: scope = SUBTREE @@ -913,9 +962,17 @@ class BaseLdapApplication(BaseDPXApplication): msg += ' ' + format_list(attributes, do_repr=True) LOG.debug(msg) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=scope, attributes=attributes, - search_filter=ldap_filter, time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=scope, attributes=attributes, + search_filter=ldap_filter, time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: if self.verbose > 4: @@ -950,9 +1007,9 @@ class BaseLdapApplication(BaseDPXApplication): self, inst, ldap_filter=None, base_dn=None, scope=None, no_complain=False): """Get DNs of all entries in the given LDAP instance and sort them.""" connect_info = self.cfg.ldap_connection[inst] + if not base_dn: base_dn = connect_info.base_dn - ldap = self.ldap_connection[inst] if scope is None: scope = SUBTREE @@ -965,10 +1022,18 @@ class BaseLdapApplication(BaseDPXApplication): if self.verbose > 1: LOG.debug(_('Using LDAP filter: {!r}').format(ldap_filter)) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, - get_operational_attributes=False, attributes=attributes, - time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, + get_operational_attributes=False, attributes=attributes, + time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: if self.verbose > 2: @@ -1006,7 +1071,6 @@ class BaseLdapApplication(BaseDPXApplication): """Get Object classes and DNs of all entries in the given LDAP instance.""" connect_info = self.cfg.ldap_connection[inst] base_dn = connect_info.base_dn - ldap = self.ldap_connection[inst] result = CIDict() attributes = ['objectClass'] @@ -1019,10 +1083,18 @@ class BaseLdapApplication(BaseDPXApplication): if self.verbose > 1: LOG.debug(_('Using LDAP filter: {!r}').format(ldap_filter)) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, - get_operational_attributes=False, attributes=attributes, - time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, + get_operational_attributes=False, attributes=attributes, + time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: if self.verbose > 5: @@ -1093,7 +1165,6 @@ class BaseLdapApplication(BaseDPXApplication): """Get the DN of the user with the given mail address in the given LDAP instance.""" connect_info = self.cfg.ldap_connection[inst] base_dn = connect_info.base_dn - ldap = self.ldap_connection[inst] result = [] @@ -1117,10 +1188,18 @@ class BaseLdapApplication(BaseDPXApplication): uri=connect_info.url, bdn=base_dn, fltr=ldap_filter) LOG.debug(msg) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, - get_operational_attributes=False, attributes=attributes, - time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, + get_operational_attributes=False, attributes=attributes, + time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: if self.verbose > 4: @@ -1146,7 +1225,6 @@ class BaseLdapApplication(BaseDPXApplication): """Get the DN of the user with the given uid (POSIX name) in the given LDAP instance.""" connect_info = self.cfg.ldap_connection[inst] base_dn = connect_info.base_dn - ldap = self.ldap_connection[inst] result = [] @@ -1170,10 +1248,18 @@ class BaseLdapApplication(BaseDPXApplication): uri=connect_info.url, bdn=base_dn, fltr=ldap_filter) LOG.debug(msg) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, - get_operational_attributes=False, attributes=attributes, - time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, + get_operational_attributes=False, attributes=attributes, + time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: if self.verbose > 4: @@ -1199,7 +1285,6 @@ class BaseLdapApplication(BaseDPXApplication): """Get the DN of the user with the given cn (common name) in the given LDAP instance.""" connect_info = self.cfg.ldap_connection[inst] base_dn = connect_info.base_dn - ldap = self.ldap_connection[inst] result = [] @@ -1223,10 +1308,18 @@ class BaseLdapApplication(BaseDPXApplication): uri=connect_info.url, bdn=base_dn, fltr=ldap_filter) LOG.debug(msg) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, - get_operational_attributes=False, attributes=attributes, - time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, + get_operational_attributes=False, attributes=attributes, + time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: if self.verbose > 4: @@ -1298,7 +1391,6 @@ class BaseLdapApplication(BaseDPXApplication): def get_entry(self, dn, inst, attributes=None, operational_attributes=False, tries=3): """Get an complete LDAP entry by the given DN in the given instance.""" connect_info = self.cfg.ldap_connection[inst] - ldap = self.ldap_connection[inst] if attributes is None: attributes = [ALL_ATTRIBUTES] @@ -1314,6 +1406,10 @@ class BaseLdapApplication(BaseDPXApplication): cur_try = 0 e_msg = None while cur_try < tries: + + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] e_msg = None cur_try += 1 try: @@ -1321,6 +1417,7 @@ class BaseLdapApplication(BaseDPXApplication): search_base=dn, search_scope=BASE, attributes=attributes, get_operational_attributes=operational_attributes, search_filter=sfilter, time_limit=self.cfg.ldap_timeout) + except (LDAPSocketReceiveError, LDAPSocketOpenError) as e: e_msg = str(e) if cur_try >= tries: @@ -1328,6 +1425,15 @@ class BaseLdapApplication(BaseDPXApplication): LOG.debug(_('Waiting because of a failing read operation.')) time.sleep(self.wait_on_read_error) + except LDAPSessionTerminatedByServerError as e: + msg = _('Session terminated on reading entry {dn!r} from instance {i!r}:').format( + dn=dn, i=inst) + ' ' + str(e) + raise LdapSessionError(msg) + + finally: + if not self.single_session: + self.disconnect_instance(inst) + if e_msg: msg = _('Error on reading entry {dn!r} from instance {inst!r}:').format( dn=dn, inst=inst) + ' ' + e_msg @@ -1384,8 +1490,7 @@ class BaseLdapApplication(BaseDPXApplication): def add_entry(self, inst, dn, object_classes, target_entry, ldap=None): """Create a LDAP entry.""" connect_info = self.cfg.ldap_connection[inst] - if not ldap: - ldap = self.ldap_connection[inst] + keep_ldap = False if self.verbose > 2: msg = _('Creating entry {dn!r} on {uri}:').format( @@ -1397,6 +1502,13 @@ class BaseLdapApplication(BaseDPXApplication): LOG.info(_('Simulation mode - entry will not be created.')) return True + if ldap: + keep_ldap = True + else: + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + try: req_status, req_result, req_response, req_whatever = ldap.add( dn, object_class=object_classes, attributes=target_entry) @@ -1406,6 +1518,9 @@ class BaseLdapApplication(BaseDPXApplication): msg += '\nobjectClasses:\n' + pp(object_classes) msg += '\nAttributes:\n' + pp(target_entry) raise WriteLDAPItemError(msg) + finally: + if not self.single_session or not keep_ldap: + self.disconnect_instance(inst) # Example result on a not successful modification: # { 'description': 'objectClassViolation', @@ -1434,8 +1549,7 @@ class BaseLdapApplication(BaseDPXApplication): def modify_entry(self, inst, dn, changes, ldap=None): """Mofify an existing LDAP entry.""" connect_info = self.cfg.ldap_connection[inst] - if not ldap: - ldap = self.ldap_connection[inst] + keep_ldap = False if self.verbose > 1: msg = _('Applying changes on {uri} to DN {dn!r}:').format( @@ -1446,6 +1560,13 @@ class BaseLdapApplication(BaseDPXApplication): LOG.info(_('Simulation mode - changes are not applied.')) return True + if ldap: + keep_ldap = True + else: + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + try: req_status, req_result, req_response, req_whatever = ldap.modify(dn, changes) except LDAPException as e: @@ -1453,6 +1574,9 @@ class BaseLdapApplication(BaseDPXApplication): dn=dn, c=e.__class__.__name__, e=e) msg += '\n' + _('Changes:') + '\n' + pp(changes) raise WriteLDAPItemError(msg) + finally: + if not self.single_session or not keep_ldap: + self.disconnect_instance(inst) # Example result on a not successful modification: # { 'description': 'objectClassViolation', @@ -1480,8 +1604,7 @@ class BaseLdapApplication(BaseDPXApplication): def delete_entry(self, inst, dn, ldap=None): """Delete a LDAP entry.""" connect_info = self.cfg.ldap_connection[inst] - if not ldap: - ldap = self.ldap_connection[inst] + keep_ldap = False msg = _('Deleting LDAP entry {dn!r} on {uri} ...').format( uri=connect_info.url, dn=dn) @@ -1491,12 +1614,22 @@ class BaseLdapApplication(BaseDPXApplication): LOG.info(_('Simulation mode - deletion will not be executed.')) return True + if ldap: + keep_ldap = True + else: + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + try: req_status, req_result, req_response, req_whatever = ldap.delete(dn) except LDAPException as e: msg = _('Deletion of {dn!r} was NOT successfull - {c}: {e}').format( c=e.__class__.__name__, e=e) raise DeleteLDAPItemError(msg) + finally: + if not self.single_session or not keep_ldap: + self.disconnect_instance(inst) if self.verbose > 1: LOG.debug(_('Deletion status: {!r}.').format(req_status)) @@ -1516,7 +1649,6 @@ class BaseLdapApplication(BaseDPXApplication): def get_group_memberships(self, inst, dn, base_dn=None): """Return a list with DNs of all groups containing the given DN as a member.""" connect_info = self.cfg.ldap_connection[inst] - ldap = self.ldap_connection[inst] if not base_dn: base_dn = connect_info.base_dn @@ -1531,10 +1663,18 @@ class BaseLdapApplication(BaseDPXApplication): uri=connect_info.url, bdn=base_dn, fltr=ldap_filter) LOG.debug(msg) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, - get_operational_attributes=False, attributes=attributes, - time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, + get_operational_attributes=False, attributes=attributes, + time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: for entry in req_response: @@ -1550,7 +1690,6 @@ class BaseLdapApplication(BaseDPXApplication): def get_unique_group_memberships(self, inst, dn, base_dn=None): """Return a list with DNs of all groups containing the given DN as a unique member.""" connect_info = self.cfg.ldap_connection[inst] - ldap = self.ldap_connection[inst] if not base_dn: base_dn = connect_info.base_dn @@ -1565,10 +1704,18 @@ class BaseLdapApplication(BaseDPXApplication): uri=connect_info.url, bdn=base_dn, fltr=ldap_filter) LOG.debug(msg) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, - get_operational_attributes=False, attributes=attributes, - time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, + get_operational_attributes=False, attributes=attributes, + time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: for entry in req_response: @@ -1584,7 +1731,6 @@ class BaseLdapApplication(BaseDPXApplication): def get_posix_group_memberships(self, inst, uid, base_dn=None): """Return a list with DNs of all POSIX groups containing the given UID as a member.""" connect_info = self.cfg.ldap_connection[inst] - ldap = self.ldap_connection[inst] if not base_dn: base_dn = connect_info.base_dn @@ -1599,10 +1745,18 @@ class BaseLdapApplication(BaseDPXApplication): uri=connect_info.url, bdn=base_dn, fltr=ldap_filter) LOG.debug(msg) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, - get_operational_attributes=False, attributes=attributes, - time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, + get_operational_attributes=False, attributes=attributes, + time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: for entry in req_response: @@ -1618,7 +1772,6 @@ class BaseLdapApplication(BaseDPXApplication): def get_sudo_group_memberships(self, inst, uid, base_dn=None): """Return a list with DNs of all sudo roles containing the given UID as a member.""" connect_info = self.cfg.ldap_connection[inst] - ldap = self.ldap_connection[inst] if not base_dn: base_dn = connect_info.base_dn @@ -1633,10 +1786,18 @@ class BaseLdapApplication(BaseDPXApplication): uri=connect_info.url, bdn=base_dn, fltr=ldap_filter) LOG.debug(msg) - req_status, req_result, req_response, req_whatever = ldap.search( - search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, - get_operational_attributes=False, attributes=attributes, - time_limit=self.cfg.ldap_timeout) + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.search( + search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter, + get_operational_attributes=False, attributes=attributes, + time_limit=self.cfg.ldap_timeout) + finally: + if not self.single_session: + self.disconnect_instance(inst) if req_status: for entry in req_response: diff --git a/lib/pp_admintools/app/mirror_ldap.py b/lib/pp_admintools/app/mirror_ldap.py index 960df25..a1522d5 100644 --- a/lib/pp_admintools/app/mirror_ldap.py +++ b/lib/pp_admintools/app/mirror_ldap.py @@ -31,13 +31,14 @@ from ldap3.core.exceptions import LDAPSocketReceiveError from .ldap import BaseLdapApplication from .ldap import LdapAppError from .ldap import LdapReadError +from .ldap import LdapSessionError from .. import pp from ..argparse_actions import LimitedFloatOptionAction from ..argparse_actions import NonNegativeIntegerOptionAction from ..config.mirror_ldap import MirrorLdapConfiguration from ..xlate import XLATOR -__version__ = '1.1.1' +__version__ = '1.1.2' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -338,8 +339,9 @@ class MirrorLdapApplication(BaseLdapApplication): self.empty_line() - except LdapReadError as e: - msg = _('Got a {}:').format('LdapReadError') + ' ' + str(e) + except (LdapReadError, LdapSessionError) as e: + cls = e.__class__.__name__ + msg = _('Got a {}:').format(cls) + ' ' + str(e) LOG.error(msg) self.exit(11)