From 7d2e84feeacb46f1a9fcee4705d8a9b423fbc1e3 Mon Sep 17 00:00:00 2001 From: ralf Date: Fri, 9 Aug 2024 14:10:23 +0200 Subject: [PATCH] * LDAP/AD: automatic retry, if connection to LDAP server was lost --- api/src/Accounts/Import.php | 21 ++++++----------- api/src/Contacts/Ldap.php | 46 +++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/api/src/Accounts/Import.php b/api/src/Accounts/Import.php index 7d87944fe7..385526ffcf 100644 --- a/api/src/Accounts/Import.php +++ b/api/src/Accounts/Import.php @@ -91,7 +91,7 @@ class Import ]; } catch (\Exception $e) { - $this->logger('Error: '.$e->getMessage(), 'fatal'); + $this->logger('Error: '.$e->getMessage().' ('.$e->getCode().')', 'fatal'); throw $e; } } @@ -273,7 +273,6 @@ class Import $start = ['', 500, &$cookie]; // cookie must be a reference! do { - $contact = $reconnected = null; foreach ($this->contacts->search('', false, '', ['account_lid', 'jpegphoto'], '', '', 'AND', $start, $filter) as $contact) { // if we have a regexp to filter the DN, continue on non-match @@ -580,15 +579,8 @@ class Import // remember the users we imported, to be able to delete the ones we dont unset($sql_users[$account_id]); } - /* check if connection was somehow lost / timed out and reconnect - if ($initial_import && !isset($contact) && ldap_errno($this->contacts->ds) === -1) - { - $this->contacts->ds = $this->accounts->ldap_connection(true); - $reconnected = true; - $this->logger("Reconnected to LDAP server", 'info'); - }*/ } - while ($reconnected || $start[2] !== ''); + while ($start[2] !== ''); if ($set_members) { @@ -668,7 +660,8 @@ class Import } } catch(\Exception $e) { - $this->logger($e->getMessage(), 'fatal'); + _egw_log_exception($e); + $this->logger($e->getMessage().' ('.$e->getCode().')', 'fatal'); $GLOBALS['egw']->accounts = $frontend; throw $e; } @@ -898,7 +891,7 @@ class Import } } catch (\Exception $e) { - $this->logger("Error deleting no longer existing $type '$account_lid' (#$account_id): ".$e->getMessage(), 'error'); + $this->logger("Error deleting no longer existing $type '$account_lid' (#$account_id): ".$e->getMessage().' ('.$e->getCode().')', 'error'); } $GLOBALS['egw']->accounts = $backup_accounts; @@ -1001,11 +994,11 @@ class Import _egw_log_exception($e); // disable async job, something is not configured correct self::installAsyncJob(); - $import->logger('Async job for periodic import canceled', 'fatal'); + $import->logger('Async job for periodic import canceled: '.$e->getMessage().' ('.$e->getCode().')', 'fatal'); } catch (\Exception $e) { _egw_log_exception($e); - $import->logger('Error: '.$e->getMessage(), 'fatal'); + $import->logger('Error: '.$e->getMessage().' ('.$e->getCode().')', 'fatal'); } } diff --git a/api/src/Contacts/Ldap.php b/api/src/Contacts/Ldap.php index 9d05a6993f..4d180da3a2 100644 --- a/api/src/Contacts/Ldap.php +++ b/api/src/Contacts/Ldap.php @@ -394,30 +394,36 @@ class Ldap $this->connect(); } + /** + * @var bool $admin parameter of last call to connect, to be able to reconnect + */ + protected bool $admin_connection; + /** * connect to LDAP server * * @param boolean $admin =false true (re-)connect with admin not user credentials, eg. to modify accounts */ - function connect($admin = false) + function connect($admin = false, $reconnect=false) { - if ($admin) + if (($this->admin_connection = $admin)) { - $this->ds = Api\Ldap::factory(); + $this->ds = Api\Ldap::factory(true, '', '', '', $reconnect); } // if ldap is NOT the contact repository, we only do accounts and need to use the account-data elseif (substr($GLOBALS['egw_info']['server']['contact_repository'],-4) != 'ldap') // not (ldap or sql-ldap) { $this->ldap_config['ldap_contact_host'] = $this->ldap_config['ldap_host']; $this->allContactsDN = $this->ldap_config['ldap_context']; - $this->ds = Api\Ldap::factory(); + $this->ds = Api\Ldap::factory(true, '', '', '', $reconnect); } else { $this->ds = Api\Ldap::factory(true, $this->ldap_config['ldap_contact_host'], $GLOBALS['egw_info']['user']['account_dn'], - $GLOBALS['egw_info']['user']['passwd'] + $GLOBALS['egw_info']['user']['passwd'], + $reconnect ); } } @@ -1276,17 +1282,27 @@ class Ldap $this->total = 0; } - if($_addressbooktype == self::ALL || $_ldapContext == $this->allContactsDN) + // retry, if connection to LDAP is lost + for($retry=1; $retry >= 0; --$retry) { - $result = ldap_search($this->ds, $_ldapContext, $_filter, $_attributes, null, null, null, null, $control); - } - else - { - $result = ldap_list($this->ds, $_ldapContext, $_filter, $_attributes, null, null, null, null, $control); - } - if(!$result || ($entries = ldap_get_entries($this->ds, $result)) === false) - { - throw new \Exception(ldap_error($this->ds) ?: 'Unable to retrieve LDAP result', ldap_errno($this->ds)); + if ($_addressbooktype == self::ALL || $_ldapContext == $this->allContactsDN) + { + $result = ldap_search($this->ds, $_ldapContext, $_filter, $_attributes, null, null, null, null, $control); + } + else + { + $result = ldap_list($this->ds, $_ldapContext, $_filter, $_attributes, null, null, null, null, $control); + } + if (!$result || ($entries = ldap_get_entries($this->ds, $result)) === false) + { + // retry after reconnect, if connection to LDAP server is lost + if ($retry >= 0 && in_array(ldap_errno($this->ds), [91, -1], true)) + { + $this->connect($this->admin_connection, true); + continue; + } + throw new \Exception(ldap_error($this->ds) ?: 'Unable to retrieve LDAP result', ldap_errno($this->ds)); + } } $this->total += $entries['count']; //error_log(__METHOD__."('$_ldapContext', '$_filter', ".array2string($_attributes).", $_addressbooktype) result of $entries[count]");